Created
November 26, 2018 14:52
-
-
Save berkes/4fc3ad04e9bb7f304fde4624207b717c to your computer and use it in GitHub Desktop.
CouponManager v0.1.0
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
pragma solidity ^0.4.22; | |
contract CouponManager { | |
enum Answers { NoAnswer, Approved, Denied, Pending } | |
struct Batch { | |
string description; | |
uint expiresAt; | |
uint24 amount; | |
uint24 free; | |
uint payPerPublication; | |
uint payPerClaim; | |
address issuer; | |
bool isLocked; | |
uint remainingBalance; | |
mapping(address => uint24) reservations; | |
mapping(address => uint24) claimsFor; | |
} | |
struct Claim { | |
Answers answer; | |
uint24 couponBatchId; | |
uint24 amount; | |
address publisher; | |
address vendor; | |
} | |
uint24 private currentBatchId; | |
uint private currentClaimId; | |
mapping(uint24 => Batch) public couponBatch; | |
mapping(uint => Claim) private claims; | |
mapping(address => uint) public balances; | |
event Issued(uint24 couponBatchId, address issuer, uint amount); | |
event Reserved(uint24 couponBatchId, address publisher, uint amount); | |
event Claimed (uint claimId, uint24 couponBatchId, address vendor, address publisher, uint amount); | |
event Acknowledged (uint claimId, Answers answer); | |
modifier notExpired(uint24 couponBatchId) { | |
require(couponBatch[couponBatchId].expiresAt > now); | |
_; | |
} | |
function issue(string _name, | |
uint _expiresAt, | |
uint24 _amount, | |
uint _payPerPublication, | |
uint _payPerClaim) public payable { | |
require(_amount > 0); | |
require(_payPerPublication > 0); | |
require(_payPerClaim > 0); | |
require((_amount * (_payPerPublication + _payPerClaim)) == msg.value); | |
currentBatchId += 1; | |
couponBatch[currentBatchId] = Batch( | |
_name, | |
_expiresAt, | |
_amount, | |
_amount, // At initialization 'free' equals amount: all coupons are free | |
_payPerPublication, | |
_payPerClaim, | |
msg.sender, | |
false, | |
_amount | |
); | |
emit Issued(currentBatchId, msg.sender, _amount); | |
} | |
function reserve(uint24 couponBatchId, uint24 _amount) public notExpired(couponBatchId) { | |
require(couponBatch[couponBatchId].free >= _amount); | |
// TODO: replace addition and substraction with SafeMath to avoid overflows. | |
couponBatch[couponBatchId].free -= _amount; | |
couponBatch[couponBatchId].reservations[msg.sender] += _amount; | |
emit Reserved(couponBatchId, msg.sender, _amount); | |
} | |
function claim(uint24 couponBatchId, | |
uint24 _amount, | |
address publisher) public notExpired(couponBatchId) { | |
// Check that there are enough claimable coupons left on the reservation | |
// for this publisher. | |
// TODO: has an overflow issue, in which we cannot ensure that a - b > 0. | |
Batch storage batch = couponBatch[couponBatchId]; | |
require((batch.reservations[publisher] - | |
batch.claimsFor[publisher]) > _amount); | |
currentClaimId += 1; | |
// Set the acknowledgement for this claim to the default value. | |
// This allows us to differ between 0 - noAnswer and 3 - Pending | |
claims[currentClaimId] = Claim( | |
Answers.Pending, | |
couponBatchId, | |
_amount, | |
publisher, | |
msg.sender | |
); | |
// TODO: Is now stored twice; once in claimsFor and once in claims. | |
// claims.filter((claim) => claim.publisher == publisher).sum is the | |
// same as this amount. | |
batch.claimsFor[publisher] += _amount; | |
emit Claimed(currentClaimId, couponBatchId, msg.sender, publisher, _amount); | |
} | |
function acknowledge(uint24 claimId, bool answer) public { | |
Claim memory acknowledgebleClaim = claims[claimId]; | |
// Checking for Undecided determins wethe no aswer was given yet; | |
// and also the claimId was registered as claim already. | |
require(acknowledgebleClaim.answer == Answers.Pending); | |
Batch storage batch = couponBatch[acknowledgebleClaim.couponBatchId]; | |
require(batch.issuer == msg.sender); | |
require(batch.expiresAt > now); | |
uint payToPublisher = acknowledgebleClaim.amount * batch.payPerPublication; | |
uint payToVendor = acknowledgebleClaim.amount * batch.payPerClaim; | |
// TODO: determine whether we want As Cheap As Possible, or | |
// equalized gas-costs: currently, denying is cheaper than | |
// approving. This may influence the honesty. | |
if (answer) { // True == Approved | |
claims[claimId].answer = Answers.Approved; | |
// TODO: extract to helper methods. | |
// TODO: handle integer overflows with SafeMath | |
balances[acknowledgebleClaim.publisher] += payToPublisher; | |
balances[acknowledgebleClaim.vendor] += payToVendor; | |
batch.remainingBalance -= (payToPublisher + payToVendor); | |
} else { | |
claims[claimId].answer = Answers.Denied; | |
} | |
emit Acknowledged(claimId, claims[claimId].answer); | |
} | |
function lock(uint24 couponBatchId) public { | |
Batch storage batch = couponBatch[couponBatchId]; | |
// The current blocktime has passed the expiresAt. | |
require(couponBatch[couponBatchId].expiresAt < now); | |
// Ensure we can run lock only once | |
require(batch.isLocked == false); | |
// Only Issuer can run | |
require(batch.issuer == msg.sender); | |
batch.isLocked = true; | |
balances[batch.issuer] += batch.remainingBalance; | |
} | |
function withdraw(uint _amount) public { | |
require(balances[msg.sender] >= _amount); | |
balances[msg.sender] -= _amount; | |
msg.sender.transfer(_amount); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment