diff --git a/contracts/gift-lib/.solhint.json b/contracts/gift-lib/.solhint.json new file mode 100644 index 0000000..d7c3de9 --- /dev/null +++ b/contracts/gift-lib/.solhint.json @@ -0,0 +1,3 @@ +{ + "extends": "solhint:default" +} diff --git a/contracts/gift-lib/giftcontract.sol b/contracts/gift-lib/giftcontract.sol new file mode 100644 index 0000000..d2c9049 --- /dev/null +++ b/contracts/gift-lib/giftcontract.sol @@ -0,0 +1,361 @@ +//generated by www.structuredeth.com/gift + +// to-do: Update to 0.5.x; +pragma solidity ^0.4.26; + +interface CompoundERC20 { + function approve(address spender, uint256 amount) external returns(bool); + + function mint(uint256 mintAmount) external returns(uint256); + + function redeem(uint redeemTokens) external returns(uint); + + function totalSupply() public view returns(uint supply); + + function balanceOf(address _owner) public view returns(uint256 balance); + + function transfer(address _to, uint _value) public returns(bool success); + + function transferFrom(address _from, address _to, uint _value) public returns(bool success); + + function exchangeRateStored() public view returns(uint256 exchangeRate); +} + +interface GiftRegistry { + function addGift(address contractAddress, uint256 initialAmount); +} + +interface IKyberNetworkProxy { + function maxGasPrice() external view returns(uint); + + function getUserCapInWei(address user) external view returns(uint); + + function getUserCapInTokenWei(address user, ERC20 token) external view returns(uint); + + function enabled() external view returns(bool); + + function info(bytes32 id) external view returns(uint); + + function getExpectedRate( + ERC20 src, + ERC20 dest, + uint srcQty + ) external view returns(uint expectedRate, uint slippageRate); + + function tradeWithHint( + ERC20 src, + uint srcAmount, + ERC20 dest, + address destAddress, + uint maxDestAmount, + uint minConversionRate, + address walletId, + bytes hint + ) external payable returns(uint); + + function swapEtherToToken(ERC20 token, uint minRate) external payable returns(uint); + + function swapTokenToEther(ERC20 token, uint tokenQty, uint minRate) external returns(uint); +} + + +library SafeMath { + function mul(uint256 a, uint256 b) internal constant returns(uint256) { + uint256 c = a * b; + assert(a == 0 || c / a == b); + return c; + } + + function div(uint256 a, uint256 b) internal constant returns(uint256) { + assert(b > 0); // Solidity automatically throws when dividing by 0 + uint256 c = a / b; + assert(a == b * c + a % b); // There is no case in which this doesn't hold + return c; + } + + function sub(uint256 a, uint256 b) internal constant returns(uint256) { + assert(b <= a); + return a - b; + } + + function add(uint256 a, uint256 b) internal constant returns(uint256) { + uint256 c = a + b; + assert(c >= a); + return c; + } +} + + +interface ERC20 { + function totalSupply() public view returns(uint supply); + + function balanceOf(address _owner) public view returns(uint balance); + + function transfer(address _to, uint _value) public returns(bool success); + + function transferFrom(address _from, address _to, uint _value) public returns(bool success); + + function approve(address _spender, uint _value) public returns(bool success); + + function allowance(address _owner, address _spender) public view returns(uint remaining); + + function decimals() public view returns(uint digits); + event Approval(address indexed _owner, address indexed _spender, uint _value); +} + + +contract GiftOfCompound { + + + // to-do: Fix scoping of variables; + // to-do: Clean up flow on entry functions + // to-do: re-compose functions for better compartmentalization + // to-do: write abstract interface for GiftOf contracts + + using SafeMath + for uint256; + address theRecipient; + address theSender; + bytes PERM_HINT; + uint256 initialCDaiAmount; + uint256 theInterestRecipient; + uint256 theInterestSender; + uint256 initialDaiAmount; + uint256 initialcDaiDaiRate; + uint256 startedWithGiftAmount; + uint256 internal PRECISION; + uint256 valueChange2Result; + address cOffRamp; + CompoundERC20 cdai; + GiftRegistry giftRegistration; + + modifier onlyGiftGroup() { + if (msg.sender != theSender && msg.sender != theRecipient) { + throw; + } + _; + } + + modifier onlyCOffRamp() { + if (msg.sender != cOffRamp) { + throw; + } + _; + } + + + //if smeone sends eth to this contract, throw it because it will just end up getting locked forever + function() payable { + throw; + } + + constructor(address recipient, uint256 interestRecipient, uint256 interestSender) public payable { + + if (msg.value <= 0) { + throw; + } + + theSender = msg.sender; + theRecipient = recipient; + PRECISION = 10 ** 27; + theInterestSender = interestSender; + theInterestRecipient = interestRecipient; + + //sum of the interest percentage must be 100 so everyone can get their funds + if (theInterestRecipient.add(theInterestSender) != 100) { + throw; + } + + startedWithGiftAmount = 0; + initialCDaiAmount = giftWrap(); + cOffRamp = 0x4C8A4aEbB3F28F9e50BDF16eD8491A1FF2CFB6fe; + address regContractAddr = 0xb83353ddea4995c3c330cf1803fc1b9f07c0de88; + giftRegistration = GiftRegistry(regContractAddr); + giftRegistration.addGift(this, initialCDaiAmount); + + } + + function redeemAll(address _to) returns(bool) { + uint256 canSendAmount = amountEntitledTo(msg.sender); + transfer(_to, canSendAmount); + return true; + + } + + function redeemAllToCentral(address _to) returns(bool) { + uint256 canSendAmount = amountEntitledTo(msg.sender); + transferKeepCDai(cOffRamp, canSendAmount); + return true; + } + + function transferKeepCDai(address _to, uint256 _value) onlyGiftGroup returns(bool) { + + uint256 userHasAccessTo = amountEntitledTo(msg.sender); + ERC20 dai = ERC20(0x89d24a6b4ccb1b6faa2625fe562bdd9a23260359); + + // you do not enough entitlement to the interest + + if (_value > userHasAccessTo) { + throw; + } else { + uint256 currentCDai = cdai.balanceOf(this); + require(dai.transfer(_to, currentCDai)); + } + + /** + * set initial amount to current amount so that people can keep + * withdrawing and we can know if they are entiteld to the interest amount + */ + initialCDaiAmount = cdai.balanceOf(this); + return true; + } + + function transfer(address _to, uint256 _value) onlyGiftGroup returns(bool) { + + uint256 userHasAccessTo = amountEntitledTo(msg.sender); + ERC20 dai = ERC20(0x89d24a6b4ccb1b6faa2625fe562bdd9a23260359); + + //you do not enough entitlement to the interest + if (_value > userHasAccessTo) { + revert(); + } else { + cdai.redeem(_value); + uint256 currentDai = dai.balanceOf(this); + require(dai.transfer(_to, currentDai)); + } + + /** + * set initial amount to current amount so that people can keep + * withdrawing and we can know if they are entiteld to the interest amount + */ + initialCDaiAmount = cdai.balanceOf(this); + + return true; + } + + function giftWrap() internal returns(uint256) { + ERC20 dai = ERC20(0x89d24a6b4ccb1b6faa2625fe562bdd9a23260359); + address kyberProxyAddress = 0x818E6FECD516Ecc3849DAf6845e3EC868087B755; + IKyberNetworkProxy kyberProxy = IKyberNetworkProxy(kyberProxyAddress); + cdai = CompoundERC20(0xf5dce57282a584d2746faf1593d3121fcac444dc); + theRecipient.send(1500000000000000); + uint256 ethAmount1 = msg.value.sub(1500000000000000); + PERM_HINT = "PERM"; + ERC20 eth = ERC20(0x00eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee); + uint daiAmount = kyberProxy.tradeWithHint.value(ethAmount1)( + eth, + ethAmount1, + dai, + this, + 8000000000000000000000000000000000000000000000000000000000000000, + 0, + 0x0000000000000000000000000000000000000004, + PERM_HINT + ); + dai.approve(address(cdai), 8000000000000000000000000000000000000000000000000000000); + cdai.mint(daiAmount); + uint256 cdaiAmount = cdai.balanceOf(this); + startedWithGiftAmount = cdaiAmount; + initialDaiAmount = daiAmount; + initialcDaiDaiRate = cdai.exchangeRateStored(); + + return cdaiAmount; + } + + function changeCOffRamp(address newCOffRamp) onlyCOffRamp returns(bool) { + cOffRamp = newCOffRamp; + } + + function amountEntitledTo(address qAddress) constant returns(uint256) { + uint256 initialExchangeRate = initialcDaiDaiRate; + uint multiplier = 10000000; + uint256 currentExchangeRate = cdai.exchangeRateStored().mul(multiplier); + uint256 valueChange = currentExchangeRate.div(initialExchangeRate); + uint256 valueChange2 = initialCDaiAmount.mul(valueChange).div(multiplier); + uint256 totalInterestEarned = valueChange2.sub(initialCDaiAmount); + uint256 usersPercentage; + + valueChange2Result = valueChange2; + + if (qAddress == theRecipient) { + usersPercentage = theInterestSender; + } else if (qAddress == theSender) { + usersPercentage = theInterestSender; + } else { + return 0; + } + + uint256 tInterestEntitledTo = totalInterestEarned.mul(usersPercentage).div(100); + uint256 amountITo; + + + // to-do: compartmentalize this if-web into internal function + + if (qAddress == theRecipient) { + amountITo = initialCDaiAmount.sub(tInterestEntitledTo); + } + + if (qAddress == theSender) { + if (initialCDaiAmount == startedWithGiftAmount) { + //nothing has been redeemed, so sender can refund + amountITo = initialCDaiAmount; + } else { + amountITo = tInterestEntitledTo; + } + } + + return amountITo; + + } + + function getStartedWithGiftAmount() constant external returns(uint256) { + return startedWithGiftAmount; + } + + function getStartedWithDaiValueAmount() constant external returns(uint256) { + return initialDaiAmount; + } + + function getStartedWithCDaiDaiRate() constant external returns(uint256) { + return initialcDaiDaiRate; + } + + function getRecipient() constant external returns(address) { + return theRecipient; + } + + function getSender() constant external returns(address) { + return theSender; + } + + function percentageInterestEntitledTo(address qAddress) constant external returns(uint256) { + uint256 usersPercentage; + + if (qAddress == theRecipient) { + usersPercentage = theInterestRecipient; + } else if (qAddress == theSender) { + usersPercentage = theInterestSender; + } else { + usersPercentage = 0; + } + + return usersPercentage; + } + + function valueChangeVal() constant external returns(uint256) { + + uint256 initialExchangeRate = initialcDaiDaiRate; + uint multiplier = 10000000; + uint256 currentExchangeRate = cdai.exchangeRateStored().mul(multiplier); + uint256 valueChange = currentExchangeRate.div(initialExchangeRate); + uint256 valueChange2 = initialCDaiAmount.mul(valueChange).div(multiplier); + uint256 totalInterestEarned = valueChange2.sub(initialCDaiAmount); + + return totalInterestEarned; + } + + function currentGiftAmount() constant external returns(uint256) { + uint256 cDaiMinted = cdai.balanceOf(this); + return cDaiMinted; + } +}