Navigation Menu

Skip to content

Instantly share code, notes, and snippets.

@berkes
Created November 26, 2018 14:52
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save berkes/4fc3ad04e9bb7f304fde4624207b717c to your computer and use it in GitHub Desktop.
Save berkes/4fc3ad04e9bb7f304fde4624207b717c to your computer and use it in GitHub Desktop.
CouponManager v0.1.0
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