Skip to content
This repository has been archived by the owner on Jul 24, 2020. It is now read-only.

Commit

Permalink
Add Question contract
Browse files Browse the repository at this point in the history
Increased time cannot be reseted according to trufflesuite/ganache-cli-archive#390
  • Loading branch information
davidyuk committed Oct 17, 2017
1 parent 40893cd commit f7a30e7
Show file tree
Hide file tree
Showing 2 changed files with 199 additions and 0 deletions.
76 changes: 76 additions & 0 deletions truffle/contracts/Question.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
pragma solidity ^0.4.15;

import "./ERC20Token.sol";

contract Question {
uint public constant version = 1;

struct Charity {
string name;
address addr;
}

ERC20Interface token;
Charity[] public charities;
string public question;
string public twitterAccount;
uint public deadline;
string public tweetUrl;
mapping(address => uint256) public donorDonations;
uint public donorCount;
uint256 public donations;

modifier beforeDeadline() {
require(now < deadline);
_;
}

function getCharitiesCount() constant returns (uint) {
return charities.length;
}

function Question(ERC20Interface _token, string _question, string _twitterAccount, uint _deadline) {
token = _token;
require(bytes(_question).length != 0);
require(bytes(_twitterAccount).length != 0);
// todo check twitter account more carefully
require(now < _deadline);

charities.push(Charity({ name: 'Test charity 1',
addr: 0xfA491DF8780761853D127A9f7b2772D688A0E3B5 }));
charities.push(Charity({ name: 'Test charity 2',
addr: 0x45992982736870Fe45c41049C5F785d4E4cc38Ec }));
charities.push(Charity({ name: 'Test charity 3',
addr: 0xfA491DF8780761853D127A9f7b2772D688A0E3B5 }));
charities.push(Charity({ name: 'Test charity 4',
addr: 0x45992982736870Fe45c41049C5F785d4E4cc38Ec }));

question = _question;
twitterAccount = _twitterAccount;
deadline = _deadline;
}

function increase(uint256 amount) beforeDeadline {
require(token.transferFrom(msg.sender, this, amount));
if (0 == donorDonations[msg.sender]) donorCount += 1;
donations += amount;
donorDonations[msg.sender] += amount;
}

function answer(string _tweetUrl) beforeDeadline {
require(bytes(_tweetUrl).length != 0);
// todo check tweet more carefully, get charity id
tweetUrl = _tweetUrl;
assert(token.transfer(charities[0].addr, donations));
}

function revertDonation() {
require(bytes(tweetUrl).length == 0);
require(now >= deadline);
require(donorDonations[msg.sender] != 0);
assert(token.transfer(msg.sender, donorDonations[msg.sender]));
donorCount -= 1;
donations -= donorDonations[msg.sender];
donorDonations[msg.sender] = 0;
}
}
123 changes: 123 additions & 0 deletions truffle/test/Question.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
/* global artifacts contract it assert web3 */

const { assertError } = require('./common');

const ERC20Token = artifacts.require('ERC20Token');
const Question = artifacts.require('Question');

const increaseTime = (seconds = 0) => web3.currentProvider.send({
jsonrpc: '2.0', method: 'evm_increaseTime', params: [seconds] }).result;

// https://github.com/ethereumjs/testrpc/issues/390
const increasedSeconds = increaseTime();
const testBeforeNowDate = Math.floor(Date.now() / 1000) + (increasedSeconds - 5);
const testAfterNowDate = Math.floor(Date.now() / 1000) + (increasedSeconds + 5);
const testQuestion = 'test_question';
const testAccount = 'test_account';
const testAnswer = 'test_answer';

contract('Question', (accounts) => {
it('can\'t be deployed with unset question', () =>
Question.new(ERC20Token.address, '', testAccount, testAfterNowDate)
.then(assert.fail, assertError));

it('can\'t be deployed with unset account', () =>
Question.new(ERC20Token.address, testQuestion, '', testAfterNowDate)
.then(assert.fail, assertError));

it('can\'t be deployed with deadline before current date', () =>
Question.new(ERC20Token.address, testQuestion, testAccount, testBeforeNowDate)
.then(assert.fail, assertError));

it('should be deployable', () =>
Question.new(ERC20Token.address, testQuestion, testAccount, testAfterNowDate)
.then(instance =>
Promise.all([
instance.version.call().then(c => assert.equal(c, 1)),
instance.getCharitiesCount.call().then(c => assert.equal(c, 4)),
instance.question.call().then(q => assert.equal(q, testQuestion)),
instance.twitterAccount.call().then(t => assert.equal(t, testAccount)),
instance.deadline.call().then(d => assert.equal(d, testAfterNowDate)),
])));

it('can\'t increase after deadline', () =>
Question.new(ERC20Token.address, testQuestion, testAccount, testBeforeNowDate)
.then(question => question.increase(0))
.then(assert.fail, assertError));

const newDonatedQuestion = amount =>
Promise.all([
Question.new(ERC20Token.address, testQuestion, testAccount, testAfterNowDate),
ERC20Token.deployed(),
])
.then(([question, token]) =>
token.approve(question.address, amount)
.then(() => question.increase(amount))
.then(() => question));

it('increase', () => {
const amount = 1;
return newDonatedQuestion(amount)
.then(question =>
Promise.all([
question.donorDonations.call(accounts[0]).then(d => assert.equal(d, amount)),
question.donorCount.call().then(c => assert.equal(c, 1)),
question.donations.call().then(d => assert.equal(d, amount)),
]));
});

it('can\'t answer after deadline', () =>
Question.new(ERC20Token.address, testQuestion, testAccount, testBeforeNowDate)
.then(question => question.answer(testAnswer))
.then(assert.fail, assertError));

it('answer', () =>
newDonatedQuestion(1)
.then(question =>
question.answer(testAnswer)
.then(() => question.tweetUrl.call()))
.then(t => assert.equal(t, testAnswer)));

it('answer with transfer to charity', () => {
const amount = 1;
return Promise.all([
newDonatedQuestion(amount),
ERC20Token.deployed(),
])
.then(([question, token]) =>
question.charities.call(0)
.then(([, charityAddress]) =>
token.balanceOf.call(charityAddress)
.then(charityBalance =>
question.answer(testAnswer)
.then(() => token.balanceOf.call(charityAddress))
.then(newCharityBalance =>
assert.equal(newCharityBalance.comparedTo(charityBalance.plus(amount)), 0)))));
});

it('can\'t revert donation before deadline', () => {
const amount = 1;
return newDonatedQuestion(amount)
.then(question => question.revertDonation())
.then(assert.fail, assertError);
});

it('revert donation', () => {
const amount = 1;
return Promise.all([
newDonatedQuestion(amount)
.then((token) => {
increaseTime(10);
return token;
}),
ERC20Token.deployed(),
])
.then(([question, token]) =>
token.balanceOf.call(accounts[0])
.then(accountBalance =>
question.revertDonation()
.then(() => token.balanceOf.call(accounts[0]))
.then(newAccountBalance =>
assert.equal(newAccountBalance.comparedTo(accountBalance.plus(amount)), 0))));
});
});

0 comments on commit f7a30e7

Please sign in to comment.