SingularDTV: A Decentralized “Netflix” on Ethereum has announced a bug bounty.
From Weekend Bug Bounty – 5 BTC for Major Bugs! 10 BTC for Fatal Flaws! self.ethereum
Weekend Bug Bounty – 5 BTC for Major Bugs! 10 BTC for Fatal Flaws!
We’ve had several participants and a few false alarms, but so far no one has found a bug in our Smart Contract System yet. We’re challenging anyone out there, over the weekend, to find flaws in our system. Anonymous submissions welcome!
Open for submissions NOW! Deadline for submissions is Tuesday, 12:01 am, September 6th.
Contact Email your submissions to: bounty@singulardtv.com
Don’t forget to include your BTC or ETH address so you can be rewarded. (If more than 1 address is specified, only one will be used at the discretion of the bounty program administrators.)
Major bugs will be rewarded 5 BTC or ETH equivalent. Much higher rewards are possible (up to 10 BTC or ETH equivalent) in case of very severe vulnerabilities.
For questions use the forum https://forum.singulardtv.com/t/singulardtv-bug-bounty/47 or email bounty@singulardtv.com.
DETAILS
The SingularDTV Token Launch spec is at: https://singulardtv.com/resources/default/pdf/SingularDTV-TokenLaunchSpecs.pdf
Most of the rules on https://bounty.ethereum.org apply. For example: First come, first serve. Issues that have already been submitted by another user or are already known, such as these, to the team are not eligible for bounty rewards. Scope of SingularDTV Bounty Program
In scope: https://github.com/ConsenSys/singulardtv-contracts
• SingularDTVCrowdfunding.sol
• SingularDTVFund.sol
• SingularDTVToken.sol
• StandardToken.sol
https://github.com/ConsenSys/singulardtv-contracts
• https://github.com/ConsenSys/eth-lightwallet/tree/2a61eab456bb2c0e97c41be209887c8ca9ad43bb/lib
• https://gist.github.com/miladmostavi/508f1628e543d10b314d901b8fd9097d
Out of scope: • MistWallet.sol
• Bugs related to Internet Explorer • All browser rendering bugs that don’t affect the display of critical information such as ETH, SNGLS • Most user experience improvements on the frontend
Again, the spec is at: https://singulardtv.com/resources/default/pdf/SingularDTV-TokenLaunchSpecs.pdf
Examples of what’s in scope: • Being able to withdraw more ETH than contributed • Being able to obtain more tokens (SNGLS) than expected • Being able to obtain SNGLS from someone without their permission • Demonstrating that the workshop can transfer their SNGLS before 2 years • Being able to put SingularDTVCrowdfunding in emergency state by making the checkInvariants() throw • Bugs in eth-lightwallet that lead to loss or theft of ETH • Bugs causing a transaction to be sent that was different from what user confirmed: for example, user transfers 10 SNGLS in the UI, but exactly 10 wasn’t transferred.
Examples of what’s out of scope: • Being able to softWithdraw another person’s revenue • Most user experience improvements on the frontend, for example some part of the website doesn’t update unless the page is refreshed
Thank you! bounty@singulardtv.com
Source Code
Source code from github.com/ConsenSys/singulardtv-contracts/contracts @ 02:38 Sep 3 2016 UTC.
AbstractCampaign.sol
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
contract Campaign { /// @notice the campaign name /// @return contractual metadata which specifies the campaign name as a string function name() constant returns(string) {} /// @notice use to determine the contribution method abi /// @return will return a string that is the exact contributeMethodABI function contributeMethodABI() constant returns(string) {} /// @notice use to determine the contribution method abi /// @return will return a string that is the exact contributeMethodABI function refundMethodABI() constant returns(string) {} /// @notice use to determine the contribution method abi /// @return will return a string that is the exact contributeMethodABI function payoutMethodABI() constant returns(string) {} /// @notice use to determine the beneficiary destination for the campaign /// @return the beneficiary address that will receive the campaign payout function beneficiary() constant returns(address) {} /// @notice the time at which the campaign fails or succeeds /// @return the uint unix timestamp at which time the campaign expires function expiry() constant returns(uint256 timestamp) {} /// @notice the goal the campaign must reach in order for it to succeed /// @return the campaign funding goal specified in wei as a uint256 function fundingGoal() constant returns(uint256 amount) {} /// @notice the goal the campaign must reach in order for it to succeed /// @return the campaign funding goal specified in wei as a uint256 function amountRaised() constant returns(uint256 amount) {} // Campaign events event ContributionMade (address _contributor); event RefundPayoutClaimed(uint256 _contributionID); event BeneficiaryPayoutClaimed (address _beneficiary, uint256 _payoutAmount); } |
AbstractSingularDTVCrowdfunding.sol
1 2 3 4 5 6 7 8 9 |
contract SingularDTVCrowdfunding { function oneYearPassed() returns (bool); function startDate() returns (uint); function CROWDFUNDING_PERIOD() returns (uint); function TOKEN_TARGET() returns (uint); function valuePerShare() returns (uint); function fundBalance() returns (uint); function campaignEndedSuccessfully() returns (bool); } |
AbstractToken.sol
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
/// Implements ERC 20 Token standard: https://github.com/ethereum/EIPs/issues/20 /// @title Abstract token contract - Functions to be implemented by token contracts. /// @author Stefan George - <stefan.george@consensys.net> contract Token { // This is not an abstract function, because solc won't recognize generated getter functions for public variables as functions function totalSupply() constant returns (uint256 supply) {} function balanceOf(address owner) constant returns (uint256 balance); function transfer(address to, uint256 value) returns (bool success); function transferFrom(address from, address to, uint256 value) returns (bool success); function approve(address spender, uint256 value) returns (bool success); function allowance(address owner, address spender) constant returns (uint256 remaining); event Transfer(address indexed from, address indexed to, uint256 value); event Approval(address indexed owner, address indexed spender, uint256 value); } |
AbstractSingularDTVToken.sol
1 2 3 4 5 |
import "AbstractToken.sol"; contract SingularDTVToken is Token { function issueTokens(address _for, uint tokenCount) returns (bool); } |
AbstractSingularDTVFund.sol
1 2 3 4 |
contract SingularDTVFund { function workshop() returns (address); function softWithdrawRevenueFor(address forAddress) returns (uint); } |
SingularDTVCrowdfunding.sol
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 |
import "AbstractSingularDTVToken.sol"; import "AbstractSingularDTVFund.sol"; /// @title Crowdfunding contract - Implements crowdfunding functionality. /// @author Stefan George - <stefan.george@consensys.net> contract SingularDTVCrowdfunding { /* * External contracts */ SingularDTVToken public singularDTVToken; SingularDTVFund public singularDTVFund; /* * Constants */ uint constant public CAP = 1000000000; // 1B tokens is the maximum amount of tokens uint constant public CROWDFUNDING_PERIOD = 4 weeks; // 1 month uint constant public TOKEN_LOCKING_PERIOD = 1 years; // 1 years uint constant public TOKEN_TARGET = 534000000; // 34M Tokens more than the initial 500M, around 42,500 ETH /* * Enums */ enum Stages { CrowdfundingGoingAndGoalNotReached, CrowdfundingEndedAndGoalNotReached, CrowdfundingGoingAndGoalReached, CrowdfundingEndedAndGoalReached } /* * Storage */ address public owner; uint public startDate; uint public fundBalance; uint public baseValue = 1250 szabo; // 0.00125 ETH uint public valuePerShare = baseValue; // 0.00125 ETH // investor address => investment in Wei mapping (address => uint) public investments; // Initialize stage Stages public stage = Stages.CrowdfundingGoingAndGoalNotReached; /* * Modifiers */ modifier noEther() { if (msg.value > 0) { throw; } _ } modifier onlyOwner() { // Only owner is allowed to do this action. if (msg.sender != owner) { throw; } _ } modifier minInvestment() { // User has to invest at least the ether value of one share. if (msg.value < valuePerShare) { throw; } _ } modifier atStage(Stages _stage) { if (stage != _stage) { throw; } _ } modifier atStageOR(Stages _stage1, Stages _stage2) { if (stage != _stage1 && stage != _stage2) { throw; } _ } modifier timedTransitions() { uint crowdfundDuration = now - startDate; if (crowdfundDuration >= 22 days) { valuePerShare = baseValue * 1500 / 1000; } else if (crowdfundDuration >= 18 days) { valuePerShare = baseValue * 1375 / 1000; } else if (crowdfundDuration >= 14 days) { valuePerShare = baseValue * 1250 / 1000; } else if (crowdfundDuration >= 10 days) { valuePerShare = baseValue * 1125 / 1000; } else { valuePerShare = baseValue; } if (crowdfundDuration >= CROWDFUNDING_PERIOD) { if (stage == Stages.CrowdfundingGoingAndGoalNotReached) { stage = Stages.CrowdfundingEndedAndGoalNotReached; } else if (stage == Stages.CrowdfundingGoingAndGoalReached) { stage = Stages.CrowdfundingEndedAndGoalReached; } } _ } /* * Contract functions */ /// dev Validates invariants. function checkInvariants() constant internal { if (fundBalance > this.balance) { throw; } } /// @dev Can be triggered if an invariant fails. function emergencyCall() external noEther returns (bool) { if (fundBalance > this.balance) { if (this.balance > 0 && !singularDTVFund.workshop().send(this.balance)) { throw; } return true; } return false; } /// @dev Allows user to fund the campaign if campaign is still going and cap not reached. Returns share count. function fund() external timedTransitions atStageOR(Stages.CrowdfundingGoingAndGoalNotReached, Stages.CrowdfundingGoingAndGoalReached) minInvestment returns (uint) { uint tokenCount = msg.value / valuePerShare; // Token count is rounded down. Investment should be multiples of valuePerShare. if (singularDTVToken.totalSupply() + tokenCount > CAP) { // User wants to buy more shares than available. Set shares to possible maximum. tokenCount = CAP - singularDTVToken.totalSupply(); } uint investment = tokenCount * valuePerShare; // Ether invested by backer. // Send change back to user. if (msg.value > investment && !msg.sender.send(msg.value - investment)) { throw; } // Update fund's and user's balance and total supply of shares. fundBalance += investment; investments[msg.sender] += investment; if (!singularDTVToken.issueTokens(msg.sender, tokenCount)) { // Tokens could not be issued. throw; } // Update stage if (stage == Stages.CrowdfundingGoingAndGoalNotReached) { if (singularDTVToken.totalSupply() >= TOKEN_TARGET) { stage = Stages.CrowdfundingGoingAndGoalReached; } } // not an else clause for the edge case that the CAP and TOKEN_TARGET are reached with one big funding if (stage == Stages.CrowdfundingGoingAndGoalReached) { if (singularDTVToken.totalSupply() == CAP) { stage = Stages.CrowdfundingEndedAndGoalReached; } } checkInvariants(); return tokenCount; } /// @dev Allows user to withdraw his funding if crowdfunding ended and target was not reached. Returns success. function withdrawFunding() external noEther timedTransitions atStage(Stages.CrowdfundingEndedAndGoalNotReached) returns (bool) { // Update fund's and user's balance and total supply of shares. uint investment = investments[msg.sender]; investments[msg.sender] = 0; fundBalance -= investment; // Send funds back to user. if (investment > 0 && !msg.sender.send(investment)) { throw; } checkInvariants(); return true; } /// @dev Withdraws funding for workshop. Returns success. function withdrawForWorkshop() external noEther timedTransitions atStage(Stages.CrowdfundingEndedAndGoalReached) returns (bool) { uint value = fundBalance; fundBalance = 0; if (value > 0 && !singularDTVFund.workshop().send(value)) { throw; } checkInvariants(); return true; } /// @dev Sets token value in Wei. /// @param valueInWei New value. function changeBaseValue(uint valueInWei) external noEther onlyOwner returns (bool) { baseValue = valueInWei; return true; } /// @dev Returns if 1 year passed since beginning of crowdfunding. function oneYearPassed() constant external noEther returns (bool) { return now - startDate >= TOKEN_LOCKING_PERIOD; } /// @dev Returns if campaign ended successfully. function campaignEndedSuccessfully() constant external noEther returns (bool) { if (stage == Stages.CrowdfundingEndedAndGoalReached) { return true; } return false; } // updateStage allows calls to receive correct stage. It can be used for transactions but is not part of the regular crowdfunding routine. // It is not marked as constant because timedTransitions modifier is altering state and constant is not yet enforced by solc. /// @dev returns correct stage, even if a function with timedTransitions modifier has not yet been called successfully. function updateStage() external timedTransitions noEther returns (Stages) { return stage; } /// @dev Setup function sets external contracts' addresses. /// @param singularDTVFundAddress Crowdfunding address. /// @param singularDTVTokenAddress Token address. function setup(address singularDTVFundAddress, address singularDTVTokenAddress) external onlyOwner noEther returns (bool) { if (address(singularDTVFund) == 0 && address(singularDTVToken) == 0) { singularDTVFund = SingularDTVFund(singularDTVFundAddress); singularDTVToken = SingularDTVToken(singularDTVTokenAddress); return true; } return false; } /// @dev Contract constructor function sets owner and start date. function SingularDTVCrowdfunding() noEther { // Set owner address owner = msg.sender; // Set start-date of crowdfunding startDate = now; } /// @dev Fallback function always fails. Use fund function to fund the contract with Ether. function () { throw; } } |
SingularDTVFund.sol
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 |
import "AbstractSingularDTVToken.sol"; import "AbstractSingularDTVCrowdfunding.sol"; /// @title Fund contract - Implements revenue distribution. /// @author Stefan George - <stefan.george@consensys.net> contract SingularDTVFund { /* * External contracts */ SingularDTVToken public singularDTVToken; SingularDTVCrowdfunding public singularDTVCrowdfunding; /* * Storage */ address public owner; address constant public workshop = {{MistWallet}}; uint public totalRevenue; // User's address => Revenue at time of withdraw mapping (address => uint) public revenueAtTimeOfWithdraw; // User's address => Revenue which can be withdrawn mapping (address => uint) public owed; /* * Modifiers */ modifier noEther() { if (msg.value > 0) { throw; } _ } modifier onlyOwner() { // Only guard is allowed to do this action. if (msg.sender != owner) { throw; } _ } modifier campaignEndedSuccessfully() { if (!singularDTVCrowdfunding.campaignEndedSuccessfully()) { throw; } _ } /* * Contract functions */ /// @dev Deposits revenue. Returns success. function depositRevenue() external campaignEndedSuccessfully returns (bool) { totalRevenue += msg.value; return true; } /// @dev Withdraws revenue share for user. Returns revenue share. /// @param forAddress Shareholder's address. function calcRevenue(address forAddress) internal returns (uint) { return singularDTVToken.balanceOf(forAddress) * (totalRevenue - revenueAtTimeOfWithdraw[forAddress]) / singularDTVToken.totalSupply(); } /// @dev Withdraws revenue share for user. Returns revenue share. function withdrawRevenue() external noEther returns (uint) { uint value = calcRevenue(msg.sender) + owed[msg.sender]; revenueAtTimeOfWithdraw[msg.sender] = totalRevenue; owed[msg.sender] = 0; if (value > 0 && !msg.sender.send(value)) { throw; } return value; } /// @dev Credits revenue share to owed balance. /// @param forAddress Shareholder's address. function softWithdrawRevenueFor(address forAddress) external noEther returns (uint) { uint value = calcRevenue(forAddress); revenueAtTimeOfWithdraw[forAddress] = totalRevenue; owed[forAddress] += value; return value; } /// @dev Setup function sets external contracts' addresses. /// @param singularDTVTokenAddress Token address. function setup(address singularDTVCrowdfundingAddress, address singularDTVTokenAddress) external noEther onlyOwner returns (bool) { if (address(singularDTVCrowdfunding) == 0 && address(singularDTVToken) == 0) { singularDTVCrowdfunding = SingularDTVCrowdfunding(singularDTVCrowdfundingAddress); singularDTVToken = SingularDTVToken(singularDTVTokenAddress); return true; } return false; } /// @dev Contract constructor function sets guard and initial token balances. function SingularDTVFund() noEther { // Set owner address owner = msg.sender; } } |
SingularDTVToken.sol
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 |
import "StandardToken.sol"; import "AbstractSingularDTVFund.sol"; import "AbstractSingularDTVCrowdfunding.sol"; /// @title Token contract - Implements token issuance. /// @author Stefan George - <stefan.george@consensys.net> contract SingularDTVToken is StandardToken { /* * External contracts */ SingularDTVFund constant singularDTVFund = SingularDTVFund({{SingularDTVFund}}); SingularDTVCrowdfunding constant singularDTVCrowdfunding = SingularDTVCrowdfunding({{SingularDTVCrowdfunding}}); /* * Token meta data */ string constant public name = "SingularDTV"; string constant public symbol = "SNGLS"; uint8 constant public decimals = 0; /* * Modifiers */ modifier noEther() { if (msg.value > 0) { throw; } _ } modifier workshopWaitedOneYear() { // Workshop can only transfer shares after a two years period. if (msg.sender == singularDTVFund.workshop() && !singularDTVCrowdfunding.oneYearPassed()) { throw; } _ } modifier isCrowdfundingContract () { // Only crowdfunding contract is allowed to proceed. if (msg.sender != address(singularDTVCrowdfunding)) { throw; } _ } /* * Contract functions */ /// @dev Crowdfunding contract issues new tokens for address. Returns success. /// @param _for Address of receiver. /// @param tokenCount Number of tokens to issue. function issueTokens(address _for, uint tokenCount) external isCrowdfundingContract returns (bool) { if (tokenCount == 0) { return false; } balances[_for] += tokenCount; totalSupply += tokenCount; return true; } /// @dev Transfers sender's tokens to a given address. Returns success. /// @param to Address of token receiver. /// @param value Number of tokens to transfer. function transfer(address to, uint256 value) noEther workshopWaitedOneYear returns (bool) { // Both parties withdraw their revenue first singularDTVFund.softWithdrawRevenueFor(msg.sender); singularDTVFund.softWithdrawRevenueFor(to); return super.transfer(to, value); } /// @dev Allows allowed third party to transfer tokens from one address to another. Returns success. /// @param from Address from where tokens are withdrawn. /// @param to Address to where tokens are sent. /// @param value Number of tokens to transfer. function transferFrom(address from, address to, uint256 value) noEther workshopWaitedOneYear returns (bool) { // Both parties withdraw their revenue first singularDTVFund.softWithdrawRevenueFor(from); singularDTVFund.softWithdrawRevenueFor(to); return super.transferFrom(from, to, value); } /// @dev Contract constructor function sets initial token balances. function SingularDTVToken() noEther { // Set initial share distribution balances[singularDTVFund.workshop()] = 400000000; // ~400M // Series A investors balances[0x0196b712a0459cbee711e7c1d34d2c85a9910379] = 5000000; balances[0x0f94dc84ce0f5fa2a8cc8d27a6969e25b5a39273] = 200000; balances[0x122b7eb5f629d806c8adb0baa0560266abb3ec80] = 450000; balances[0x13870d30fcdb7d7ae875668f2a1219225295d57c] = 50000; balances[0x26640e826547bc700b8c7a9cc2c1c39a4ab3cbb3] = 900000; balances[0x26bbfc6b23bc36e84447f061c6804f3a8b1a3698] = 250000; balances[0x2d37383a45b5122a27efade69f7180eee4d965da] = 1270000; balances[0x2e79b81121193d55c4934c0f32ad3d0474ca7b9c] = 4200000; balances[0x3114844fc0e3de03963bbd1d983ba17ca89ad010] = 5000000; balances[0x378e6582e4e3723f7076c7769eef6febf51258e1] = 680000; balances[0x3e18530a4ee49a0357ffc8e74c08bfdee3915482] = 2490000; balances[0x43fed1208d25ca0ef5681a5c17180af50c19f826] = 100000; balances[0x4f183b18302c0ac5804b8c455018efc51af15a56] = 10000; balances[0x55a886834658ccb6f26c39d5fdf6d833df3a276a] = 100000; balances[0x5faa1624422db662c654ab35ce57bf3242888937] = 5000000; balances[0x6407b662b306e2353b627488da952337a5a0bbaa] = 5000000; balances[0x66c334fff8c8b8224b480d8da658ca3b032fe625] = 10000000; balances[0x6c24991c6a40cd5ad6fab78388651fb324b35458] = 250000; balances[0x781ba492f786b2be48c2884b733874639f50022c] = 500000; balances[0x79b48f6f1ac373648c509b74a2c04a3281066457] = 2000000; balances[0x835898804ed30e20aa29f2fe35c9f225175b049f] = 100000; balances[0x93c56ea8848150389e0917de868b0a23c87cf7b1] = 2790000; balances[0x93f959df3df3c6ee01ee9748327b881b2137bf2a] = 450000; balances[0x9adc0215372e4ffd8c89621a6bd9cfddf230349f] = 550000; balances[0xae4dbd3dae66722315541d66fe9457b342ac76d9] = 500000; balances[0xbae02fe006f115e45b372f2ddc053eedca2d6fff] = 1800000; balances[0xcc835821f643e090d8157de05451b416cd1202c4] = 300000; balances[0xce75342b92a7d0b1a2c6e9835b6b85787e12e585] = 670000; balances[0xd2b388467d9d0c30bab0a68070c6f49c473583a0] = 990000; balances[0xdca0724ddde95bbace1b557cab4375d9a813da49] = 3500000; balances[0xe3ef62165b60cac0fcbe9c2dc6a03aab4c5c8462] = 150000; balances[0xe4f7d5083baeea7810b6d816581bb0ee7cd4b6f4] = 10560000; balances[0xef08eb55d3482973c178b02bd4d5f2cea420325f] = 80000; balances[0xfdecc9f2ee374cedc94f72ab4da2de896ce58c19] = 5000000; balances[0xe5ff71dc1dea8cd2552eec59e9a5e8813da9bb01] = 29110000; totalSupply = 500000000; // 500M } } |
SingularDTVWeifund.sol
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
import "AbstractCampaign.sol"; import "AbstractSingularDTVFund.sol"; import "AbstractSingularDTVCrowdfunding.sol"; /// @title Crowdfunding contract - Implements crowdfunding functionality. /// @author Stefan George - <stefan.george@consensys.net> contract SingularDTVWeifund is Campaign { /* * External contracts */ SingularDTVFund constant singularDTVFund = SingularDTVFund({{SingularDTVFund}}); SingularDTVCrowdfunding constant singularDTVCrowdfunding = SingularDTVCrowdfunding({{SingularDTVCrowdfunding}}); string constant public name = "SingularDTV Campaign"; string constant public contributeMethodABI = "fund()"; string constant public refundMethodABI = "withdrawFunding()"; string constant public payoutMethodABI = "withdrawForWorkshop()"; /// @notice use to determine the beneficiary destination for the campaign /// @return the beneficiary address that will receive the campaign payout function beneficiary() constant returns(address) { return singularDTVFund.workshop(); } /// @notice the time at which the campaign fails or succeeds /// @return the uint unix timestamp at which time the campaign expires function expiry() constant returns(uint256 timestamp) { return singularDTVCrowdfunding.startDate() + singularDTVCrowdfunding.CROWDFUNDING_PERIOD(); } /// @notice the goal the campaign must reach in order for it to succeed /// @return the campaign funding goal specified in wei as a uint256 function fundingGoal() constant returns(uint256 amount) { return singularDTVCrowdfunding.TOKEN_TARGET() * singularDTVCrowdfunding.valuePerShare(); } /// @notice the goal the campaign must reach in order for it to succeed /// @return the campaign funding goal specified in wei as a uint256 function amountRaised() constant returns(uint256 amount) { return singularDTVCrowdfunding.fundBalance(); } } |
StandardToken.sol
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 |
import "AbstractToken.sol"; contract StandardToken is Token { /* * Data structures */ mapping (address => uint256) balances; mapping (address => mapping (address => uint256)) allowed; uint256 public totalSupply; /* * Read and write storage functions */ /// @dev Transfers sender's tokens to a given address. Returns success. /// @param _to Address of token receiver. /// @param _value Number of tokens to transfer. function transfer(address _to, uint256 _value) returns (bool success) { if (balances[msg.sender] >= _value && _value > 0) { balances[msg.sender] -= _value; balances[_to] += _value; Transfer(msg.sender, _to, _value); return true; } else { return false; } } /// @dev Allows allowed third party to transfer tokens from one address to another. Returns success. /// @param _from Address from where tokens are withdrawn. /// @param _to Address to where tokens are sent. /// @param _value Number of tokens to transfer. function transferFrom(address _from, address _to, uint256 _value) returns (bool success) { if (balances[_from] >= _value && allowed[_from][msg.sender] >= _value && _value > 0) { balances[_to] += _value; balances[_from] -= _value; allowed[_from][msg.sender] -= _value; Transfer(_from, _to, _value); return true; } else { return false; } } /// @dev Returns number of tokens owned by given address. /// @param _owner Address of token owner. function balanceOf(address _owner) constant returns (uint256 balance) { return balances[_owner]; } /// @dev Sets approved amount of tokens for spender. Returns success. /// @param _spender Address of allowed account. /// @param _value Number of approved tokens. function approve(address _spender, uint256 _value) returns (bool success) { allowed[msg.sender][_spender] = _value; Approval(msg.sender, _spender, _value); return true; } /* * Read storage functions */ /// @dev Returns number of allowed tokens for given address. /// @param _owner Address of token owner. /// @param _spender Address of token spender. function allowance(address _owner, address _spender) constant returns (uint256 remaining) { return allowed[_owner][_spender]; } } |