Skip to content

Commit 465ad3d

Browse files
committed
init
1 parent 977b8b3 commit 465ad3d

4 files changed

+223
-5
lines changed

README.md

+7-1
Original file line numberDiff line numberDiff line change
@@ -1 +1,7 @@
1-
# non-fungible-token
1+
# Non Fungible Token
2+
3+
## ERC875
4+
5+
[issue your own ERC875 token](./how-to-issue-erc875-token.md)
6+
7+
[what is ERC875](https://github.com/ethereum/EIPs/issues/875)

TicketPro.sol

+213
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,213 @@
1+
pragma solidity ^0.4.17;
2+
contract TicketPro
3+
{
4+
mapping(address => bytes32[]) inventory;
5+
uint16 ticketIndex = 0; //to track mapping in tickets
6+
address organiser;
7+
address paymaster;
8+
uint numOfTransfers = 0;
9+
string public name;
10+
string public symbol;
11+
uint8 public constant decimals = 0; //no decimals as tickets cannot be split
12+
13+
event Transfer(address indexed _to, uint16[] _indices);
14+
event TransferFrom(address indexed _from, address indexed _to, uint16[] _indices);
15+
event Trade(address indexed seller, uint16[] ticketIndices, uint8 v, bytes32 r, bytes32 s);
16+
event PassTo(uint16[] ticketIndices, uint8 v, bytes32 r, bytes32 s, address indexed recipient);
17+
18+
modifier organiserOnly()
19+
{
20+
if(msg.sender != organiser) revert();
21+
else _;
22+
}
23+
24+
modifier payMasterOnly()
25+
{
26+
if(msg.sender != paymaster) revert();
27+
else _;
28+
}
29+
30+
function() public { revert(); } //should not send any ether directly
31+
32+
33+
constructor (
34+
bytes32[] tickets,
35+
string nameOfContract,
36+
string symbolForContract,
37+
address organiserAddr,
38+
address paymasterAddr,
39+
address recipientAddr) public
40+
{
41+
name = nameOfContract;
42+
symbol = symbolForContract;
43+
organiser = organiserAddr;
44+
paymaster = paymasterAddr;
45+
inventory[recipientAddr] = tickets;
46+
}
47+
48+
function getDecimals() public pure returns(uint)
49+
{
50+
return decimals;
51+
}
52+
53+
// example: 0, [3, 4], 27, "0x9CAF1C785074F5948310CD1AA44CE2EFDA0AB19C308307610D7BA2C74604AE98", "0x23D8D97AB44A2389043ECB3C1FB29C40EC702282DB6EE1D2B2204F8954E4B451"
54+
// price is encoded in the server and the msg.value is added to the message digest,
55+
// if the message digest is thus invalid then either the price or something else in the message is invalid
56+
function trade(uint256 expiry,
57+
uint16[] ticketIndices,
58+
uint8 v,
59+
bytes32 r,
60+
bytes32 s) public payable
61+
{
62+
//checks expiry timestamp,
63+
//if fake timestamp is added then message verification will fail
64+
require(expiry > block.timestamp || expiry == 0);
65+
66+
bytes32 message = encodeMessage(msg.value, expiry, ticketIndices);
67+
address seller = ecrecover(message, v, r, s);
68+
69+
for(uint i = 0; i < ticketIndices.length; i++)
70+
{ // transfer each individual tickets in the ask order
71+
uint16 index = ticketIndices[i];
72+
assert(inventory[seller][index] != bytes32(0)); // 0 means ticket gone.
73+
inventory[msg.sender].push(inventory[seller][index]);
74+
// 0 means ticket gone.
75+
delete inventory[seller][index];
76+
}
77+
seller.transfer(msg.value);
78+
79+
emit Trade(seller, ticketIndices, v, r, s);
80+
}
81+
82+
function loadNewTickets(bytes32[] tickets) public organiserOnly
83+
{
84+
for(uint i = 0; i < tickets.length; i++)
85+
{
86+
inventory[organiser].push(tickets[i]);
87+
}
88+
}
89+
90+
function passTo(uint256 expiry,
91+
uint16[] ticketIndices,
92+
uint8 v,
93+
bytes32 r,
94+
bytes32 s,
95+
address recipient) public payMasterOnly
96+
{
97+
require(expiry > block.timestamp || expiry == 0);
98+
bytes32 message = encodeMessage(0, expiry, ticketIndices);
99+
address giver = ecrecover(message, v, r, s);
100+
for(uint i = 0; i < ticketIndices.length; i++)
101+
{
102+
uint16 index = ticketIndices[i];
103+
//needs to use revert as all changes should be reversed
104+
//if the user doesnt't hold all the tickets
105+
assert(inventory[giver][index] != bytes32(0));
106+
bytes32 ticket = inventory[giver][index];
107+
inventory[recipient].push(ticket);
108+
delete inventory[giver][index];
109+
}
110+
111+
emit PassTo(ticketIndices, v, r, s, recipient);
112+
}
113+
114+
//must also sign in the contractAddress
115+
function encodeMessage(uint value, uint expiry, uint16[] ticketIndices)
116+
internal view returns (bytes32)
117+
{
118+
bytes memory message = new bytes(84 + ticketIndices.length * 2);
119+
address contractAddress = getContractAddress();
120+
for (uint i = 0; i < 32; i++)
121+
{ // convert bytes32 to bytes[32]
122+
// this adds the price to the message
123+
message[i] = byte(bytes32(value << (8 * i)));
124+
}
125+
126+
for (i = 0; i < 32; i++)
127+
{
128+
message[i + 32] = byte(bytes32(expiry << (8 * i)));
129+
}
130+
131+
for(i = 0; i < 20; i++)
132+
{
133+
message[64 + i] = byte(bytes20(bytes20(contractAddress) << (8 * i)));
134+
}
135+
136+
for (i = 0; i < ticketIndices.length; i++)
137+
{
138+
// convert int[] to bytes
139+
message[84 + i * 2 ] = byte(ticketIndices[i] >> 8);
140+
message[84 + i * 2 + 1] = byte(ticketIndices[i]);
141+
}
142+
143+
return keccak256(message);
144+
}
145+
146+
function name() public view returns(string)
147+
{
148+
return name;
149+
}
150+
151+
function symbol() public view returns(string)
152+
{
153+
return symbol;
154+
}
155+
156+
function getAmountTransferred() public view returns (uint)
157+
{
158+
return numOfTransfers;
159+
}
160+
161+
function balanceOf(address _owner) public view returns (bytes32[])
162+
{
163+
return inventory[_owner];
164+
}
165+
166+
function myBalance() public view returns(bytes32[]){
167+
return inventory[msg.sender];
168+
}
169+
170+
function transfer(address _to, uint16[] ticketIndices) public
171+
{
172+
for(uint i = 0; i < ticketIndices.length; i++)
173+
{
174+
uint index = uint(ticketIndices[i]);
175+
assert(inventory[msg.sender][index] != bytes32(0));
176+
//pushes each element with ordering
177+
inventory[_to].push(inventory[msg.sender][index]);
178+
delete inventory[msg.sender][index];
179+
}
180+
emit Transfer(_to, ticketIndices);
181+
}
182+
183+
function transferFrom(address _from, address _to, uint16[] ticketIndices)
184+
organiserOnly public
185+
{
186+
for(uint i = 0; i < ticketIndices.length; i++)
187+
{
188+
uint index = uint(ticketIndices[i]);
189+
assert(inventory[_from][index] != bytes32(0));
190+
//pushes each element with ordering
191+
inventory[_to].push(inventory[msg.sender][index]);
192+
delete inventory[_from][index];
193+
}
194+
195+
emit TransferFrom(_from, _to, ticketIndices);
196+
}
197+
198+
function endContract() public organiserOnly
199+
{
200+
selfdestruct(organiser);
201+
}
202+
203+
function isStormBirdContract() public pure returns (bool)
204+
{
205+
return true;
206+
}
207+
208+
function getContractAddress() public view returns(address)
209+
{
210+
return this;
211+
}
212+
213+
}

how-to-issue-erc875-token.md

+3-4
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ Visit [MetaMask official website](https://metamask.io/) to install Chrome Extens
1616
1717
Here I used a test wallet generated by [ganache](http://truffleframework.com/ganache). You can find the mnemonic as following pictures:
1818

19-
todo: upload ganache screenshot
19+
![](https://github.com/HiBlock/non-fungible-token/blob/master/images/mnemonic.png)
2020

2121
In Metamask main window,
2222
- click "Import using account seed phrase" link,
@@ -31,7 +31,7 @@ Absolutely you can use any wallet to generate an address, e.g [MyEtherWallet(MEW
3131
## Choose ethereum network
3232
Yes we can choose ethereum mainnet to issue ERC875 token, but we also can have it with ropsten testnet. Here is the example with ropsten testnet.
3333

34-
todo: upload metamask network choose pic
34+
![](https://github.com/HiBlock/non-fungible-token/blob/master/images/metamask-choose-network.png)
3535

3636
### Get rEth from ropsten testnet
3737
Visit [MetaMask Ether Faucet](https://faucet.metamask.io/) to request test ether for ropsten testnet.
@@ -46,8 +46,7 @@ We will use [remix](https://remix.ethereum.org/) to deploy our new token to rops
4646

4747
### program smart contract
4848

49-
todo: upload sample token to github.
50-
Here is a sample token code you can download.
49+
Here is a [sample token code](./TicketPro.sol) you can download.
5150
- Then visit [remix](https://remix.ethereum.org/),
5251
- create a new file named TicketPro,
5352
- copy and paste sample code into remix

images/metamask-choose-network.png

157 KB
Loading

0 commit comments

Comments
 (0)