Skip to content

Commit 877cd6d

Browse files
authored
Merge pull request #114 from HQ20/new/energy
New/energy
2 parents 3840533 + 89b0957 commit 877cd6d

File tree

3 files changed

+126
-90
lines changed

3 files changed

+126
-90
lines changed
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
pragma solidity ^0.5.10;
2+
3+
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
4+
// import "@hq20/contracts/contracts/access/Whitelist.sol";
5+
import "./../../../access/Whitelist.sol";
6+
7+
8+
/**
9+
* @title Energy Market
10+
* @notice Implements a simple energy market, using ERC20 and Whitelist.
11+
* ERC20 is used to enable payments from the consumers to the distribution
12+
* network, represented by this contract, and from the distribution network
13+
* to the producers. Whitelist is used to keep a list of compliant smart
14+
* meters that communicate the production and consumption of energy.
15+
*/
16+
contract EnergyMarket is ERC20, Whitelist {
17+
18+
event EnergyProduced(address producer, uint256 time);
19+
event EnergyConsumed(address consumer, uint256 time);
20+
21+
// uint128 is used here to facilitate the price formula
22+
// Casting between uint128 and int256 never overflows
23+
// int256(uint128) - int256(uint128) never overflows
24+
mapping(uint256 => uint128) public consumption;
25+
mapping(uint256 => uint128) public production;
26+
uint128 public basePrice;
27+
28+
/**
29+
* @dev The constructor initializes the underlying currency token and the
30+
* smart meter whitelist. The constructor also mints the requested amount
31+
* of the underlying currency token to fund the network load. Also sets the
32+
* maximum energy price, used for calculating prices.
33+
*/
34+
constructor (uint256 _initialSupply, uint128 _basePrice)
35+
public
36+
ERC20()
37+
Whitelist()
38+
{
39+
_mint(address(this), _initialSupply);
40+
basePrice = _basePrice;
41+
}
42+
43+
/**
44+
* @dev The production price for each time slot.
45+
*/
46+
function getProductionPrice(uint256 _time) public view returns(uint256) {
47+
return uint256(
48+
max(
49+
0,
50+
int256(basePrice) *
51+
(3 + safeSub(production[_time], consumption[_time]))
52+
)
53+
);
54+
}
55+
56+
/**
57+
* @dev The consumption price for each time slot
58+
*/
59+
function getConsumptionPrice(uint256 _time) public view returns(uint256) {
60+
return uint256(
61+
max(
62+
0,
63+
int256(basePrice) *
64+
(3 + safeSub(consumption[_time], production[_time]))
65+
)
66+
);
67+
}
68+
69+
/**
70+
* @dev Add one energy unit to the distribution network at the specified
71+
* time and be paid the production price. Only whitelisted smart meters can
72+
* call this function.
73+
*/
74+
function produce(uint256 _time) public {
75+
require(isMember(msg.sender), "Unknown meter.");
76+
this.transfer(
77+
msg.sender,
78+
getProductionPrice(_time)
79+
);
80+
production[_time] = production[_time] + 1;
81+
emit EnergyProduced(msg.sender, _time);
82+
}
83+
84+
/**
85+
* @dev Take one energy unit from the distribution network at the specified
86+
* time by paying the consumption price. Only whitelisted smart meters can
87+
* call this function.
88+
*/
89+
function consume(uint256 _time) public {
90+
require(isMember(msg.sender), "Unknown meter.");
91+
this.transferFrom(
92+
msg.sender,
93+
address(this),
94+
getConsumptionPrice(_time)
95+
);
96+
consumption[_time] = consumption[_time] + 1;
97+
emit EnergyConsumed(msg.sender, _time);
98+
}
99+
100+
/**
101+
* @dev Returns the largest of two numbers.
102+
*/
103+
function max(int256 a, int256 b) internal pure returns (int256) {
104+
return a >= b ? a : b;
105+
}
106+
107+
/**
108+
* @dev Substracts b from a using types safely casting from uint128 to int256.
109+
*/
110+
function safeSub(uint128 a, uint128 b) internal pure returns (int256) {
111+
return int256(a) - int256(b);
112+
}
113+
}

contracts/examples/energy/EnergyMarket.sol

Lines changed: 0 additions & 79 deletions
This file was deleted.

test/examples/energy/EnergyMarket.test.ts renamed to test/drafts/examples/energy/EnergyMarket.test.ts

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import { BigNumber } from 'bignumber.js';
22
// tslint:disable-next-line:no-var-requires
3-
import { EnergyMarketInstance } from '../../../types/truffle-contracts';
3+
import { EnergyMarketInstance } from '../../../../types/truffle-contracts';
44

55
const EnergyMarket = artifacts.require(
6-
'./examples/energy/EnergyMarket.sol',
6+
'./drafts/examples/energy/EnergyMarket.sol',
77
) as Truffle.Contract<EnergyMarketInstance>;
88

99
// tslint:disable:no-var-requires
@@ -23,12 +23,14 @@ contract('EnergyMarket', (accounts) => {
2323
let energyMarket: EnergyMarketInstance;
2424

2525
const initialSupply = 1000000;
26-
const maxPrice = 10;
26+
const basePrice = 10;
27+
28+
const timeSlot = 1;
2729

2830
beforeEach(async () => {
2931
energyMarket = await EnergyMarket.new(
3032
initialSupply,
31-
maxPrice,
33+
basePrice,
3234
);
3335
await energyMarket.addMember(authorized);
3436
});
@@ -37,38 +39,38 @@ contract('EnergyMarket', (accounts) => {
3739
* @test {EnergyMarket#produce}
3840
*/
3941
it('Produce energy', async () => {
40-
expect(energyMarket.produce({ from: authorized })).to.emit('EnergyProduced').withArgs(authorized);
42+
expect(energyMarket.produce(timeSlot, { from: authorized })).to.emit('EnergyProduced').withArgs(authorized);
4143
});
4244

4345
/**
4446
* @test {EnergyMarket#produce}
4547
*/
4648
it('Produce throws with unauthorized producer', async () => {
47-
expect(energyMarket.produce({ from: unauthorized })).to.revertWith('Unknown meter.');
49+
expect(energyMarket.produce(timeSlot, { from: unauthorized })).to.revertWith('Unknown meter.');
4850
});
4951

5052
/**
5153
* @test {EnergyMarket#consume}
5254
*/
5355
it('Consume energy', async () => {
54-
await energyMarket.produce({from: authorized });
56+
await energyMarket.produce(timeSlot, {from: authorized });
5557
await energyMarket.approve(
56-
energyMarket.address, await energyMarket.getConsumptionPrice(), { from: authorized },
58+
energyMarket.address, await energyMarket.getConsumptionPrice(1), { from: authorized },
5759
);
58-
expect(energyMarket.consume({ from: authorized })).to.emit('EnergyConsumed').withArgs(authorized);
60+
expect(energyMarket.consume(timeSlot, { from: authorized })).to.emit('EnergyConsumed').withArgs(authorized);
5961
});
6062

6163
/**
6264
* @test {EnergyMarket#consume}
6365
*/
6466
it('Consume throws with unauthorized consumer', async () => {
65-
expect(energyMarket.consume({ from: unauthorized })).to.revertWith('Unknown meter.');
67+
expect(energyMarket.consume(timeSlot, { from: unauthorized })).to.revertWith('Unknown meter.');
6668
});
6769

6870
/**
6971
* @test {EnergyMarket#consume}
7072
*/
7173
it('Consume throws if consumer has insufficient balance', async () => {
72-
expect(energyMarket.consume({ from: authorized })).to.revert;
74+
expect(energyMarket.consume(timeSlot, { from: authorized })).to.revert;
7375
});
7476
});

0 commit comments

Comments
 (0)