Skip to content

Commit

Permalink
Lock interface (#251)
Browse files Browse the repository at this point in the history
Created `ILock` for all locks to share.  This is a `contract` bringing together the `interface ILockCore` and the `interface ERC721` and `Ownable`.

Added an implementation for a Lock `updateKeyPrice`.
  • Loading branch information
Hardly Difficult committed Sep 25, 2018
1 parent d33007d commit 8b60e75
Show file tree
Hide file tree
Showing 5 changed files with 172 additions and 10 deletions.
24 changes: 14 additions & 10 deletions smart-contracts/contracts/Lock.sol
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
pragma solidity 0.4.24;

import "openzeppelin-solidity/contracts/ownership/Ownable.sol";
import "./ERC721.sol";
import "./interfaces/ILock.sol";
import "./Unlock.sol";

/**
Expand All @@ -12,17 +11,10 @@ import "./Unlock.sol";
* @title The Lock contract
* @author Julien Genestoux (unlock-protocol.com)
* Eventually: implement ERC721.
* @dev The Lock smart contract is an ERC721 compatible smart contract.
* However, is has some specificities:
* - Since each address owns at most one single key, the tokenId is equal to the owner
* - Each address owns at most one single key (ERC721 allows for multiple owned NFTs)
* - When transfering the key, we actually reset the expiration date on the transfered key to now
* and assign its previous expiration date to the new owner. This is important because it prevents
* some abuse around referrals.
* TODO: consider using a _private version for each method that is being invoked by the
* public one as this seems to be a pattern.
*/
contract Lock is Ownable, ERC721 {
contract Lock is ILock {

// The struct for a key
struct Key {
Expand Down Expand Up @@ -355,6 +347,18 @@ contract Lock is Ownable, ERC721 {
{
keyByOwner[_owner].expirationTimestamp = now; // Effectively expiring the key
}

/**
* A function which lets the owner of the lock to change the price for future purchases.
*/
function updateKeyPrice(
uint _keyPrice
)
external
onlyOwner
{
keyPrice = _keyPrice;
}

/**
* @dev Returns the key's data field for a given owner.
Expand Down
File renamed without changes.
20 changes: 20 additions & 0 deletions smart-contracts/contracts/interfaces/ILock.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
pragma solidity 0.4.24;

import "openzeppelin-solidity/contracts/ownership/Ownable.sol";
import "./ERC721.sol";
import "./ILockCore.sol";


/**
* @title The Lock interface
* @author HardlyDifficult (unlock-protocol.com)
* @dev Lock smart contracts are ERC721 compatible smart contracts.
* However, they have some specificities:
* - Since each address owns at most one single key, the tokenId is equal to the owner
* - Each address owns at most one single key (ERC721 allows for multiple owned NFTs)
* - When transfering the key, we actually reset the expiration date on the transfered key to now
* and assign its previous expiration date to the new owner. This is important because it prevents
* some abuse around referrals.
*/
contract ILock is ILockCore, ERC721, Ownable {
}
88 changes: 88 additions & 0 deletions smart-contracts/contracts/interfaces/ILockCore.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
pragma solidity 0.4.24;


/**
* @title The Lock interface core methods for a Lock
* @author HardlyDifficult (unlock-protocol.com)
*/
interface ILockCore {

/**
* @dev Purchase function, public version, with no referrer.
* @param _recipient address of the recipient of the purchased key
* @param _data optional marker for the key
*/
function purchaseFor(
address _recipient,
bytes _data
)
external
payable;

/**
* @dev Purchase function, public version, with referrer.
* @param _recipient address of the recipient of the purchased key
* @param _referrer address of the user making the referral
* @param _data optional marker for the key
*/
function purchaseForFrom(
address _recipient,
address _referrer,
bytes _data
)
external
payable;

/**
* @dev Called by owner to wiwthdraw all funds from the lock.
*/
function withdraw(
)
external;

/**
* A function which lets the owner of the lock expire a users' key.
*/
function expireKeyFor(
address _owner
)
external;

/**
* A function which lets the owner of the lock to change the price for future purchases.
*/
function updateKeyPrice(
uint _keyPrice
)
external;

/**
* Public function which returns the total number of keys (both expired and valid)
*/
function outstandingKeys()
external
view
returns (uint);

/**
* @dev Returns the key's data field for a given owner.
* @param _owner address of the user for whom we search the key
*/
function keyDataFor(
address _owner
)
external
view
returns (bytes data);

/**
* @dev Returns the key's ExpirationTimestamp field for a given owner.
* @param _owner address of the user for whom we search the key
*/
function keyExpirationTimestampFor(
address _owner
)
external
view
returns (uint timestamp);
}
50 changes: 50 additions & 0 deletions smart-contracts/test/Lock/updateKeyPrice.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
const Units = require('ethereumjs-units')

const deployLocks = require('../helpers/deployLocks')
const Unlock = artifacts.require('../Unlock.sol')

let unlock, locks

contract('Lock', (accounts) => {
before(() => {
return Unlock.deployed()
.then(_unlock => {
unlock = _unlock
return deployLocks(unlock)
})
.then(_locks => {
locks = _locks
})
.then(() => {
// Increases the price from the default 0.01, 'eth'.
locks['FIRST'].updateKeyPrice(Units.convert('0.1', 'eth', 'wei'))
})
})

describe('updateKeyPrice', () => {
it('should fail if the price is not enough', () => {
return locks['FIRST']
.purchaseFor(accounts[0], 'Julien', {
value: Units.convert('0.01', 'eth', 'wei')
})
.then(() => {
assert.fail()
})
.catch(error => {
assert.equal(error.message, 'VM Exception while processing transaction: revert')
// Making sure we do not have a key set!
return locks['FIRST'].keyExpirationTimestampFor(accounts[0])
.catch(error => {
assert.equal(error.message, 'VM Exception while processing transaction: revert')
})
})
})

it('should purchase when the correct amount of ETH is sent', () => {
return locks['FIRST']
.purchaseFor(accounts[0], 'Julien', {
value: Units.convert('0.1', 'eth', 'wei')
})
})
})
})

0 comments on commit 8b60e75

Please sign in to comment.