Last active
November 23, 2018 01:15
-
-
Save Dobrokhvalov/372f03cd5e2f093b6b47445cf3a41ed0 to your computer and use it in GitHub Desktop.
This file contains 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
import './SafeMath.sol'; | |
import './Stoppable.sol'; | |
import './MyCoolNFT.sol'; | |
/** | |
* @title NFT Linkdrop Contract | |
* | |
*/ | |
contract NFTLinkdropContract is Stoppable { | |
using SafeMath for uint256; | |
address public NFT_ADDRESS; // token to distribute | |
address public LINKDROPPER; // linkdropper's address, which has NFTs to distribute | |
address public LINKDROP_VERIFICATION_ADDRESS; // special address, used on claim to verify | |
// that links signed by the linkdropper | |
event LogWithdraw( | |
address indexed transitAddress, | |
uint256 indexed tokenId, | |
address receiver, | |
uint timestamp, | |
uint256 wrappedETH | |
); | |
event LogBuy( | |
uint256 indexed tokenId, | |
bytes32 secretHash, | |
uint wrappedETH, | |
uint commissionFee | |
); | |
event LogCancel( | |
address indexed sender, | |
uint256 indexed tokenId | |
); | |
event LogWithdrawCommission(uint commissionAmount); | |
event LogChangeFixedCommissionFee( | |
uint oldCommissionFee, | |
uint newCommissionFee | |
); | |
// Mappings of transit address => receiver address if link is used. | |
mapping (address => address) usedLinks; | |
struct Gift { | |
address sender; | |
bytes32 secretHash; | |
uint wrappedETH; | |
} | |
// mapping tokenId => secretHash | |
mapping (uint => Gift) gifts; | |
uint public accruedCommission; | |
uint public commissionFee; | |
/** | |
* @dev Contructor that sets linkdrop params | |
* @param _linkdropVerificationAddress special address, used on claim to | |
* verify that links signed by the linkdropper | |
*/ | |
constructor( | |
address _linkdropVerificationAddress, | |
uint _commissionFee, | |
string _name, | |
string _symbol | |
) public { | |
LINKDROPPER = msg.sender; | |
NFT_ADDRESS = new MintableNFT(_name, _symbol); | |
LINKDROP_VERIFICATION_ADDRESS = _linkdropVerificationAddress; | |
commissionFee = _commissionFee; | |
accruedCommission = 0; | |
} | |
/** | |
* @dev Verify that address is signed with needed private key. | |
* @param _transitAddress transit address assigned to transfer | |
* @param _addressSigned address Signed address. | |
* @param _tokenId tokenId attached to link. | |
* @param _v ECDSA signature parameter v. | |
* @param _r ECDSA signature parameters r. | |
* @param _s ECDSA signature parameters s. | |
* @return True if signature is correct. | |
*/ | |
function verifyLinkPrivateKey( | |
address _transitAddress, | |
address _addressSigned, | |
uint256 _tokenId, | |
uint8 _v, | |
bytes32 _r, | |
bytes32 _s) | |
public pure returns(bool success) { | |
bytes32 prefixedHash = keccak256("\x19Ethereum Signed Message:\n32", _addressSigned, _tokenId); | |
address retAddr = ecrecover(prefixedHash, _v, _r, _s); | |
return retAddr == _transitAddress; | |
} | |
/** | |
* @dev Change relayer's fixed commission fee. | |
* Only owner can change commision fee. | |
* | |
* @param _newCommissionFee uint New relayer's fixed commission | |
* @return True if success. | |
*/ | |
function changeFixedCommissionFee(uint _newCommissionFee) | |
public | |
whenNotPaused | |
whenNotStopped | |
onlyOwner | |
returns(bool success) { | |
uint oldCommissionFee = commissionFee; | |
commissionFee = _newCommissionFee; | |
emit LogChangeFixedCommissionFee(oldCommissionFee, commissionFee); | |
return true; | |
} | |
function buyGiftLink(uint _tokenId, bytes32 _secretHash) | |
payable public | |
whenNotPaused | |
whenNotStopped | |
onlyOwner returns (bool) { | |
require(msg.value > commissionFee); | |
uint wrappedETH = msg.value.sub(commissionFee); //amount = msg.value - comission | |
// saving transfer details | |
gifts[_tokenId] = Gift( | |
msg.sender, | |
_secretHash, | |
wrappedETH | |
); | |
// accrue verifier's commission | |
accruedCommission = accruedCommission.add(commissionFee); | |
// send nft | |
MintableNFT nft = MintableNFT(NFT_ADDRESS); | |
nft.mint(address(this), _tokenId); | |
// log buy event | |
emit LogBuy(_tokenId, _secretHash, wrappedETH, commissionFee); | |
return true; | |
} | |
/** | |
* @dev Cancel transfer and get sent ether back. Only transfer sender can | |
* cancel transfer. | |
* @return True if success. | |
*/ | |
function cancelGift(uint _tokenId) public returns (bool success) { | |
Gift memory gift = gifts[_tokenId]; | |
// only sender can cancel transfer; | |
require(msg.sender == gift.sender); | |
delete gifts[_tokenId]; | |
// transfer ether to recipient's address | |
msg.sender.transfer(gift.wrappedETH); | |
// log cancel event | |
emit LogCancel(msg.sender, _tokenId); | |
return true; | |
} | |
function getGift(uint _tokenId) | |
public view returns (bytes32 secretHash, uint wrappedETH) { | |
return ( | |
gifts[_tokenId].secretHash, | |
gifts[_tokenId].wrappedETH | |
); | |
} | |
/** | |
* @dev Transfer accrued commission to verifier's address. | |
* @return True if success. | |
*/ | |
function withdrawCommission() | |
public | |
whenNotPaused | |
returns(bool success) | |
{ | |
uint commissionToTransfer = accruedCommission; | |
accruedCommission = 0; | |
owner.transfer(commissionToTransfer); // owner is verifier | |
emit LogWithdrawCommission(commissionToTransfer); | |
return true; | |
} | |
/** | |
* @dev Verify that address is signed with needed private key. | |
* @param _transitAddress transit address assigned to transfer | |
* @param _addressSigned address Signed address. | |
* @param _v ECDSA signature parameter v. | |
* @param _r ECDSA signature parameters r. | |
* @param _s ECDSA signature parameters s. | |
* @return True if signature is correct. | |
*/ | |
function verifyReceiverAddress( | |
address _transitAddress, | |
address _addressSigned, | |
uint8 _v, | |
bytes32 _r, | |
bytes32 _s) | |
public pure returns(bool success) { | |
bytes32 prefixedHash = keccak256("\x19Ethereum Signed Message:\n32", _addressSigned); | |
address retAddr = ecrecover(prefixedHash, _v, _r, _s); | |
return retAddr == _transitAddress; | |
} | |
/** | |
* @dev Verify that claim params are correct and the link key wasn't used before. | |
* @param _recipient address to receive tokens. | |
* @param _tokenId NFT's id | |
* @param _transitAddress transit address provided by the airdropper | |
* @param _keyV ECDSA signature parameter v. Signed by the airdrop transit key. | |
* @param _keyR ECDSA signature parameters r. Signed by the airdrop transit key. | |
* @param _keyS ECDSA signature parameters s. Signed by the airdrop transit key. | |
* @param _recipientV ECDSA signature parameter v. Signed by the link key. | |
* @param _recipientR ECDSA signature parameters r. Signed by the link key. | |
* @param _recipientS ECDSA signature parameters s. Signed by the link key. | |
* @return True if claim params are correct. | |
*/ | |
function checkWithdrawal( | |
address _recipient, | |
uint256 _tokenId, | |
address _transitAddress, | |
uint8 _keyV, | |
bytes32 _keyR, | |
bytes32 _keyS, | |
uint8 _recipientV, | |
bytes32 _recipientR, | |
bytes32 _recipientS) | |
public view returns(bool success) { | |
// verify that link wasn't used before | |
require(isLinkClaimed(_transitAddress) == false); | |
// verifying that key is legit and signed by LINKDROP_VERIFICATION_ADDRESS's key | |
require(verifyLinkPrivateKey(LINKDROP_VERIFICATION_ADDRESS, _transitAddress, _tokenId, _keyV, _keyR, _keyS)); | |
// verifying that recepients address signed correctly | |
require(verifyReceiverAddress(_transitAddress, _recipient, _recipientV, _recipientR, _recipientS)); | |
return true; | |
} | |
/** | |
* @dev Withdraw an nft to receiver address if withdraw params are correct. | |
* @param _recipient address to receive the nft. | |
* @param _tokenId NFT's id | |
* @param _transitAddress transit address provided by the airdropper | |
* @param _keyV ECDSA signature parameter v. Signed by the airdrop transit key. | |
* @param _keyR ECDSA signature parameters r. Signed by the airdrop transit key. | |
* @param _keyS ECDSA signature parameters s. Signed by the airdrop transit key. | |
* @param _recipientV ECDSA signature parameter v. Signed by the link key. | |
* @param _recipientR ECDSA signature parameters r. Signed by the link key. | |
* @param _recipientS ECDSA signature parameters s. Signed by the link key. | |
* @return True if NFT was successfully sent to receiver. | |
*/ | |
function withdraw( | |
address _recipient, | |
uint256 _tokenId, | |
address _transitAddress, | |
uint8 _keyV, | |
bytes32 _keyR, | |
bytes32 _keyS, | |
uint8 _recipientV, | |
bytes32 _recipientR, | |
bytes32 _recipientS | |
) | |
public | |
whenNotPaused | |
whenNotStopped | |
returns (bool success) { | |
Gift memory gift = gifts[_tokenId]; | |
// verify link | |
require(checkWithdrawal(_recipient, | |
_tokenId, | |
_transitAddress, | |
_keyV, | |
_keyR, | |
_keyS, | |
_recipientV, | |
_recipientR, | |
_recipientS)); | |
// mark link as used | |
usedLinks[_transitAddress] = _recipient; | |
// delete not needed info | |
delete gifts[_tokenId]; | |
// send nft | |
ERC721 nft = ERC721(NFT_ADDRESS); | |
nft.transferFrom(address(this), _recipient, _tokenId); | |
// transfer ether to recipient's address | |
_recipient.transfer(gift.wrappedETH); | |
// log withdrawal | |
emit LogWithdraw( | |
_transitAddress, | |
_tokenId, | |
_recipient, | |
now, | |
gift.wrappedETH | |
); | |
return true; | |
} | |
/** | |
* @dev Get boolean if link is already claimed. | |
* @param _transitAddress transit address provided to receiver by the airdropper | |
* @return True if the transit address was already used. | |
*/ | |
function isLinkClaimed(address _transitAddress) | |
public view returns (bool claimed) { | |
return linkClaimedTo(_transitAddress) != 0x0; | |
} | |
/** | |
* @dev Get receiver for claimed link | |
* @param _transitAddress transit address provided to receiver by the airdropper | |
* @return True if the transit address was already used. | |
*/ | |
function linkClaimedTo(address _transitAddress) | |
public view returns (address receiver) { | |
return usedLinks[_transitAddress]; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment