Skip to content

Commit 2abf4be

Browse files
committed
chore(refactor): removing measure and ranges to scope down contract complexity
1 parent 25a237a commit 2abf4be

File tree

3 files changed

+84
-165
lines changed

3 files changed

+84
-165
lines changed

contracts/SinglePlayerCommit.sol

Lines changed: 52 additions & 131 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,6 @@ import "@openzeppelin/contracts/access/Ownable.sol";
77
import "@openzeppelin/contracts/math/SafeMath.sol";
88
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
99

10-
//TODO Do we want console.log logging, start with Activity constructor?..
11-
1210
contract SinglePlayerCommit is Ownable {
1311
using SafeMath for uint256;
1412

@@ -21,26 +19,18 @@ contract SinglePlayerCommit is Ownable {
2119
/***************
2220
DATA TYPES
2321
***************/
24-
struct Measure {
25-
string name;
26-
bool allowed;
27-
}
28-
2922
struct Activity {
30-
string name; // e.g. "cycling"
31-
bytes32[] measures; // keys from allowedMeasures
32-
uint256[2][] ranges; // array of [min,max] goal values
23+
string name; // e.g. "cycling" with list scoped to activities supported by Strava
3324
address oracle;
3425
bool allowed;
3526
}
3627

3728
struct Commitment {
3829
address committer; // user
39-
bytes32 activity; // key from allowedActivities
40-
bytes32 measure; // key from allowedMeasures
41-
uint256 goalValue; // must be within range of Activity.measures[measureIndex]
42-
uint256 start;
43-
uint256 end;
30+
bytes32 activityKey;
31+
uint256 goal;
32+
uint256 startTime;
33+
uint256 endTime;
4434
uint256 stake; // amount of token staked, scaled by token decimals
4535
bool exists; // flag to help check if commitment exists
4636
uint256 reportedValue; // as reported by oracle
@@ -52,8 +42,8 @@ contract SinglePlayerCommit is Ownable {
5242
***************/
5343
event NewCommitment(
5444
address committer,
55-
string activity,
56-
string measure,
45+
string activityName,
46+
uint256 goal,
5747
uint256 startTime,
5848
uint256 endTime,
5949
uint256 stake
@@ -68,11 +58,8 @@ contract SinglePlayerCommit is Ownable {
6858
mapping(bytes32 => Activity) public allowedActivities;
6959
bytes32[] public activityList;
7060

71-
mapping(bytes32 => Measure) public allowedMeasures;
72-
bytes32[] public measureList;
73-
7461
mapping(address => Commitment) public commitments; // active commitments
75-
// address[] public committers; // addresses with active commitments
62+
address[] public userCommitments; // addresses with active commitments
7663

7764
mapping(address => uint256) public balances; // current token balances
7865
uint256 public committerBalance; // sum of current token balances
@@ -82,71 +69,35 @@ contract SinglePlayerCommit is Ownable {
8269
********/
8370
// constructor
8471
constructor(
85-
string memory _activity,
86-
string[] memory _measures,
87-
uint256[2][] memory _ranges,
88-
address _oracle,
72+
string[] memory _activityList,
73+
address _oracleAddress,
8974
address _token
9075
) public {
9176
console.log("Constructor called for SinglePlayerCommit contract");
9277
// set up token interface
9378
token = IERC20(_token);
79+
require(_activityList.length >= 1, "SPC::constructor - activityList empty");
9480

95-
// need to create fixed length bytes32 array to pass to _addActivity
96-
uint256 len = _measures.length;
97-
bytes32[] memory measureKeys = new bytes32[](len);
98-
99-
// register measures
100-
for (uint256 i = 0; i < len; i++) {
101-
// register the measure
102-
bytes32 measureKey = _addMeasure(_measures[i]);
103-
// add its key to the array to be passed to _addActivity
104-
measureKeys[i] = measureKey;
105-
}
106-
107-
// register activity
108-
_addActivity(_activity, measureKeys, _ranges, _oracle);
81+
// register allowed activities with corresponding oracle
82+
_addActivities(_activityList, _oracleAddress);
10983
}
11084

111-
// fallback function (if exists)
112-
// TODO
113-
11485
// view functions
11586

11687
function getActivityName(bytes32 _activityKey) public view returns (string memory) {
11788
return allowedActivities[_activityKey].name;
11889
}
11990

120-
function getActivityMeasures(bytes32 _activityKey) public view returns (string[] memory measureNames) {
121-
bytes32[] memory measures = allowedActivities[_activityKey].measures;
122-
uint256 len = measures.length;
123-
measureNames = new string[](len);
124-
for (uint256 i = 0; i < len; i++) {
125-
measureNames[i] = getMeasureName(measures[i]);
126-
}
127-
128-
return measureNames;
129-
}
130-
131-
function getMeasureName(bytes32 _measureKey) public view returns (string memory) {
132-
return allowedMeasures[_measureKey].name;
133-
}
134-
13591
// other public functions
13692
function depositAndCommit(
137-
bytes32 _activity,
138-
uint256 _measureIndex,
93+
bytes32 _activityKey,
13994
uint256 _goal,
14095
uint256 _startTime,
14196
uint256 _stake,
14297
uint256 _depositAmount
14398
) public returns (bool) {
14499
require(deposit(_depositAmount), "SPC::depositAndCommit - deposit failed");
145-
146-
require(
147-
makeCommitment(_activity, _measureIndex, _goal, _startTime, _stake),
148-
"SPC::depositAndCommit - commitment failed"
149-
);
100+
require(makeCommitment(_activityKey, _goal, _startTime, _stake), "SPC::depositAndCommit - commitment failed");
150101

151102
return true;
152103
}
@@ -165,39 +116,32 @@ contract SinglePlayerCommit is Ownable {
165116
}
166117

167118
function makeCommitment(
168-
bytes32 _activity,
169-
uint256 _measureIndex, // index of the Activity.measures array
119+
bytes32 _activityKey,
120+
// uint256 _measureIndex, // index of the Activity.measures array
170121
uint256 _goal,
171122
uint256 _startTime,
172123
uint256 _stake
173124
) public returns (bool) {
174125
console.log("makeCommitment called by %s", msg.sender);
175126

176127
require(!commitments[msg.sender].exists, "SPC::makeCommitment - msg.sender already has a commitment");
177-
require(allowedActivities[_activity].allowed, "SPC::makeCommitment - activity doesn't exist or isn't allowed");
178-
require(allowedActivities[_activity].measures.length >= _measureIndex+1, "SPC::makeCommitment - measure index out of bounds");
179-
180-
bytes32 measure = allowedActivities[_activity].measures[_measureIndex];
181-
182-
require(allowedMeasures[measure].allowed, "SPC::makeCommitment - measure doesn't exist or isn't allowed");
128+
require(
129+
allowedActivities[_activityKey].allowed,
130+
"SPC::makeCommitment - activity doesn't exist or isn't allowed"
131+
);
183132
require(_startTime > block.timestamp, "SPC::makeCommitment - commitment cannot start in the past");
184-
185-
uint256[2] storage range = allowedActivities[_activity].ranges[_measureIndex];
186-
require(_goal >= range[0], "SPC::makeCommitment - goal is too low");
187-
require(_goal <= range[1], "SPC::makeCommitment - goal is too high");
188-
133+
require(_goal >= 1, "SPC::makeCommitment - goal is too low");
189134
require(balances[msg.sender] >= _stake, "SPC::makeCommitment - insufficient token balance");
190135

191136
uint256 endTime = _startTime.add(7 days);
192137

193138
// create commitment...
194139
Commitment memory commitment = Commitment({
195140
committer: msg.sender,
196-
activity: _activity,
197-
measure: measure,
198-
goalValue: _goal,
199-
start: _startTime,
200-
end: endTime,
141+
activityKey: _activityKey,
142+
goal: _goal,
143+
startTime: _startTime,
144+
endTime: endTime,
201145
stake: _stake,
202146
exists: true,
203147
reportedValue: 0,
@@ -206,16 +150,8 @@ contract SinglePlayerCommit is Ownable {
206150

207151
// ...and add it to storage
208152
commitments[msg.sender] = commitment;
209-
// committers.push(msg.sender);
210-
211-
emit NewCommitment(
212-
msg.sender,
213-
allowedActivities[_activity].name,
214-
allowedMeasures[measure].name,
215-
_startTime,
216-
endTime,
217-
_stake
218-
);
153+
154+
emit NewCommitment(msg.sender, allowedActivities[_activityKey].name, _goal, _startTime, endTime, _stake);
219155

220156
return true;
221157
}
@@ -247,16 +183,13 @@ contract SinglePlayerCommit is Ownable {
247183
Commitment memory commitment = commitments[committer];
248184

249185
// check if commitment has ended
250-
require(commitment.end < block.timestamp, "SPC::processCommitment - commitment is still active");
186+
require(commitment.endTime < block.timestamp, "SPC::processCommitment - commitment is still active");
251187

252188
bool met = commitment.met;
253189
uint256 stake = commitment.stake;
254190

255191
// "delete" the expired commitment
256192
commitments[committer].exists = false;
257-
// remove the committer from the list of committers
258-
// committers[committer] = committers[committers.length.sub(1)];
259-
// committers.pop();
260193

261194
uint256 penalty;
262195

@@ -271,58 +204,46 @@ contract SinglePlayerCommit is Ownable {
271204
emit CommitmentEnded(committer, met, penalty);
272205
}
273206

274-
// function processCommitments() public returns (bool) {
275-
// for (uint256 i = 0; i < committers.length; i.add(1)) {
276-
// processCommitment(committers[i]);
277-
// }
278-
279-
// return true;
280-
// }
281-
282207
function ownerWithdraw(uint256 amount) public onlyOwner returns (bool) {
283208
uint256 available = token.balanceOf(address(this)).sub(committerBalance);
284209

285210
require(amount <= available, "SPC::ownerWithdraw - not enough available balance");
286-
287211
require(token.transfer(msg.sender, amount), "SPC::ownerWithdraw - token transfer failed");
288212

289213
return true;
290214
}
291215

292216
// internal functions
217+
function _addActivities(string[] memory _activityList, address oracleAddress) internal {
218+
uint256 arrayLength = _activityList.length;
293219

294-
function _addMeasure(string memory _name) internal returns (bytes32 measureKey) {
295-
Measure memory measure = Measure({ name: _name, allowed: true });
296-
297-
measureKey = keccak256(abi.encode(_name));
298-
allowedMeasures[measureKey] = measure;
299-
measureList.push(measureKey);
220+
for (uint256 i = 0; i < arrayLength; i++) {
221+
_addActivity(_activityList[i], oracleAddress);
222+
}
300223

301-
return measureKey;
224+
console.log("All provided activities added");
302225
}
303226

304-
function _addActivity(
305-
string memory _name,
306-
bytes32[] memory _measures,
307-
uint256[2][] memory _ranges,
308-
address _oracle
309-
) internal returns (bytes32 activityKey) {
310-
uint256 measuresLength = _measures.length;
311-
require(measuresLength == _ranges.length, "SPC::_addActivity - measures and ranges must have same length");
312-
313-
Activity memory activity;
314-
315-
activity.name = _name;
316-
activity.oracle = _oracle;
317-
activity.measures = _measures;
318-
activity.ranges = _ranges;
227+
function _addActivity(string memory _activityName, address _oracleAddress) internal returns (bytes32 activityKey) {
228+
bytes memory activityNameBytes = bytes(_activityName);
229+
require(activityNameBytes.length > 0, "SPC::_addActivity - _activityName empty");
230+
231+
bytes32 _activityKey = keccak256(abi.encode(_activityName));
232+
233+
Activity storage activity = allowedActivities[_activityKey];
234+
activity.name = _activityName;
235+
activity.oracle = _oracleAddress;
319236
activity.allowed = true;
320237

321-
activityKey = keccak256(abi.encode(_name));
322-
allowedActivities[activityKey] = activity;
323-
activityList.push(activityKey);
238+
console.log(
239+
"Registered activity %s, oracle %s, allowed %s",
240+
allowedActivities[_activityKey].name,
241+
allowedActivities[_activityKey].oracle,
242+
allowedActivities[_activityKey].allowed
243+
);
324244

325-
return activityKey;
245+
activityList.push(_activityKey);
246+
return _activityKey;
326247
}
327248

328249
function _changeCommitterBalance(uint256 amount, bool add) internal returns (bool) {

test/SinglePlayerCommit.deploy.ts

Lines changed: 28 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,46 @@
11
import { expect } from "chai";
2-
import { BytesLike } from "ethers/lib/utils";
2+
import { BytesLike, solidityKeccak256 } from "ethers/lib/utils";
33

44
export function shouldDeployWithInitialParameters(): void {
55

6-
it("has the 'biking' activity and it is allowed", async function () {
7-
const activityKey: BytesLike = await this.singlePlayerCommit.activityList(0);
8-
const _activityName: string = await this.singlePlayerCommit.getActivityName(activityKey);
9-
6+
it("has the 'biking' and 'cycling' activity and it is allowed", async function () {
7+
//Check biking
8+
const _activityName: string = "biking";
9+
const _firstKey: BytesLike = await this.singlePlayerCommit.activityList(0);
1010

11-
const _activity = await this.singlePlayerCommit.allowedActivities(activityKey);
11+
//TODO Find the way to get the expected Keccak256 output matching the first key in the list
12+
// const _activityKey: BytesLike = solidityKeccak256(["string"], [_activityName]);
13+
// console.log("Activitykey: ", _activityKey )
1214

13-
expect(_activityName).to.equal('biking');
15+
const _activity = await this.singlePlayerCommit.allowedActivities(_firstKey);
16+
console.log("Activity returned: ", _activity)
17+
18+
//Check running
19+
const _activityName2: string = "running";
20+
const _secondKey: BytesLike = await this.singlePlayerCommit.activityList(1);
21+
22+
const _activityKey2: BytesLike = solidityKeccak256(["string"], [_activityName2]);
23+
console.log("Activitykey: ", _activityKey2 )
24+
25+
const _activity2 = await this.singlePlayerCommit.allowedActivities(_secondKey);
26+
console.log("Activity returned: ", _activity2)
27+
28+
//Validate
1429
expect(_activity['name']).to.equal(_activityName);
15-
// expect(_activity['measures'][0]).to.equal('km');
16-
// expect(_activity['ranges']).to.equal([2,1024]);
1730
expect(_activity['oracle']).to.be.properAddress;
1831
expect(_activity['allowed']).to.be.true;
19-
// expect('getActivityName').to.be.calledOnContract(this.singlePlayerCommit);
20-
});
21-
22-
it("has no other activities", async function () {
23-
await expect(
24-
this.singlePlayerCommit.activityList(1),
25-
).to.be.revertedWith("Transaction reverted without a reason")
26-
27-
});
2832

29-
it("has the 'km' measure and it is allowed", async function () {
30-
const measureKey: BytesLike = await this.singlePlayerCommit.measureList(0);
31-
const activityMeasure: string[] = await this.singlePlayerCommit.allowedMeasures(measureKey);
32-
33-
expect(activityMeasure[0]).to.equal('km');
34-
expect(activityMeasure[1]).to.be.true;
33+
expect(_activity2['name']).to.equal(_activityName2);
34+
expect(_activity2['oracle']).to.be.properAddress;
35+
expect(_activity2['allowed']).to.be.true;
3536

3637
});
3738

38-
it("has no other measures", async function () {
39+
it("has no other activities", async function () {
3940
await expect(
40-
this.singlePlayerCommit.activityList(1),
41+
this.singlePlayerCommit.activityList(2),
4142
).to.be.revertedWith("Transaction reverted without a reason")
43+
4244
});
4345

4446
}

0 commit comments

Comments
 (0)