Skip to content

Commit

Permalink
Merge pull request #114 from HQ20/new/energy
Browse files Browse the repository at this point in the history
New/energy
  • Loading branch information
vibern0 committed Jan 10, 2020
2 parents 3840533 + 89b0957 commit 877cd6d
Show file tree
Hide file tree
Showing 3 changed files with 126 additions and 90 deletions.
113 changes: 113 additions & 0 deletions contracts/drafts/examples/energy/EnergyMarket.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
pragma solidity ^0.5.10;

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
// import "@hq20/contracts/contracts/access/Whitelist.sol";
import "./../../../access/Whitelist.sol";


/**
* @title Energy Market
* @notice Implements a simple energy market, using ERC20 and Whitelist.
* ERC20 is used to enable payments from the consumers to the distribution
* network, represented by this contract, and from the distribution network
* to the producers. Whitelist is used to keep a list of compliant smart
* meters that communicate the production and consumption of energy.
*/
contract EnergyMarket is ERC20, Whitelist {

event EnergyProduced(address producer, uint256 time);
event EnergyConsumed(address consumer, uint256 time);

// uint128 is used here to facilitate the price formula
// Casting between uint128 and int256 never overflows
// int256(uint128) - int256(uint128) never overflows
mapping(uint256 => uint128) public consumption;
mapping(uint256 => uint128) public production;
uint128 public basePrice;

/**
* @dev The constructor initializes the underlying currency token and the
* smart meter whitelist. The constructor also mints the requested amount
* of the underlying currency token to fund the network load. Also sets the
* maximum energy price, used for calculating prices.
*/
constructor (uint256 _initialSupply, uint128 _basePrice)
public
ERC20()
Whitelist()
{
_mint(address(this), _initialSupply);
basePrice = _basePrice;
}

/**
* @dev The production price for each time slot.
*/
function getProductionPrice(uint256 _time) public view returns(uint256) {
return uint256(
max(
0,
int256(basePrice) *
(3 + safeSub(production[_time], consumption[_time]))
)
);
}

/**
* @dev The consumption price for each time slot
*/
function getConsumptionPrice(uint256 _time) public view returns(uint256) {
return uint256(
max(
0,
int256(basePrice) *
(3 + safeSub(consumption[_time], production[_time]))
)
);
}

/**
* @dev Add one energy unit to the distribution network at the specified
* time and be paid the production price. Only whitelisted smart meters can
* call this function.
*/
function produce(uint256 _time) public {
require(isMember(msg.sender), "Unknown meter.");
this.transfer(
msg.sender,
getProductionPrice(_time)
);
production[_time] = production[_time] + 1;
emit EnergyProduced(msg.sender, _time);
}

/**
* @dev Take one energy unit from the distribution network at the specified
* time by paying the consumption price. Only whitelisted smart meters can
* call this function.
*/
function consume(uint256 _time) public {
require(isMember(msg.sender), "Unknown meter.");
this.transferFrom(
msg.sender,
address(this),
getConsumptionPrice(_time)
);
consumption[_time] = consumption[_time] + 1;
emit EnergyConsumed(msg.sender, _time);
}

/**
* @dev Returns the largest of two numbers.
*/
function max(int256 a, int256 b) internal pure returns (int256) {
return a >= b ? a : b;
}

/**
* @dev Substracts b from a using types safely casting from uint128 to int256.
*/
function safeSub(uint128 a, uint128 b) internal pure returns (int256) {
return int256(a) - int256(b);
}
}
79 changes: 0 additions & 79 deletions contracts/examples/energy/EnergyMarket.sol

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { BigNumber } from 'bignumber.js';
// tslint:disable-next-line:no-var-requires
import { EnergyMarketInstance } from '../../../types/truffle-contracts';
import { EnergyMarketInstance } from '../../../../types/truffle-contracts';

const EnergyMarket = artifacts.require(
'./examples/energy/EnergyMarket.sol',
'./drafts/examples/energy/EnergyMarket.sol',
) as Truffle.Contract<EnergyMarketInstance>;

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

const initialSupply = 1000000;
const maxPrice = 10;
const basePrice = 10;

const timeSlot = 1;

beforeEach(async () => {
energyMarket = await EnergyMarket.new(
initialSupply,
maxPrice,
basePrice,
);
await energyMarket.addMember(authorized);
});
Expand All @@ -37,38 +39,38 @@ contract('EnergyMarket', (accounts) => {
* @test {EnergyMarket#produce}
*/
it('Produce energy', async () => {
expect(energyMarket.produce({ from: authorized })).to.emit('EnergyProduced').withArgs(authorized);
expect(energyMarket.produce(timeSlot, { from: authorized })).to.emit('EnergyProduced').withArgs(authorized);
});

/**
* @test {EnergyMarket#produce}
*/
it('Produce throws with unauthorized producer', async () => {
expect(energyMarket.produce({ from: unauthorized })).to.revertWith('Unknown meter.');
expect(energyMarket.produce(timeSlot, { from: unauthorized })).to.revertWith('Unknown meter.');
});

/**
* @test {EnergyMarket#consume}
*/
it('Consume energy', async () => {
await energyMarket.produce({from: authorized });
await energyMarket.produce(timeSlot, {from: authorized });
await energyMarket.approve(
energyMarket.address, await energyMarket.getConsumptionPrice(), { from: authorized },
energyMarket.address, await energyMarket.getConsumptionPrice(1), { from: authorized },
);
expect(energyMarket.consume({ from: authorized })).to.emit('EnergyConsumed').withArgs(authorized);
expect(energyMarket.consume(timeSlot, { from: authorized })).to.emit('EnergyConsumed').withArgs(authorized);
});

/**
* @test {EnergyMarket#consume}
*/
it('Consume throws with unauthorized consumer', async () => {
expect(energyMarket.consume({ from: unauthorized })).to.revertWith('Unknown meter.');
expect(energyMarket.consume(timeSlot, { from: unauthorized })).to.revertWith('Unknown meter.');
});

/**
* @test {EnergyMarket#consume}
*/
it('Consume throws if consumer has insufficient balance', async () => {
expect(energyMarket.consume({ from: authorized })).to.revert;
expect(energyMarket.consume(timeSlot, { from: authorized })).to.revert;
});
});

0 comments on commit 877cd6d

Please sign in to comment.