TokenPlay Blockchain service that provides connectivity to the Ethereum Network and APIs for the TokenPlay frontend application.
We are one of the first videogame companies to demonstrate tokenization games and game assets via smart contracts.
All source code is released as is under the Apache v2 licensing.
Prerequisites: In order to set up and run the server you will need Nodejs, NPM, and MongoDB
This server relies on native modules, if you are running it on a windows system, you may need to globally install the windows-build-tools package via NPM.
Once you meet the prerequisites, clone to local project directory and install dependancies with:
npm install
This package also requires gulp, truffle, and ganache-cli(required for local blockchain only) packages to be installed globally. To install them run
npm install -g ganache-cli truffle gulp-cli
Be sure to have mongoDB running in a seperate terminal. Then to run the server on Ropsten with hot reloading:
gulp start
By default, this will use the last-deployed contract, if you need to test on Ropsten with a fresh contract, follow the deployment instructions below.
To run the server on a local blockChain, follow the deployment instructions below, and then run:
gulp start:local
- Set a mnemonic in process.env.mnemonic if you would like to use your own accounts to manage the contract. There is a test mnemonic currently set in config.js. The test Mnemonic is "donate leisure glass eye pitch seek tray update nerve fortune paper effort"
If you change this in your environment variables, be sure to change it in the commands below, and in MetaMask to ensure that you control the correct accounts in your deployment.
-
Ensure that the first account generated by your mnemonic has some ether.
-
(optional) if you are deploying to a local blockchain, open ganache in a seperate terminal with the command:
ganache-cli -i 101 -m "donate leisure glass eye pitch seek tray update nerve fortune paper effort"
This is used to control the network ID of the blockchain you create, as well as the mnemonic used to generate unlocked addresses with Ether.
For a more realistic simulation of the Ethereum block chain, consider running ganache-cli with -b 90
to put up to a 90 second delay on transactions.
-
Run the following steps
cd truffle truffle build
for Ropsten:
truffle migrate --network ropsten
or for a local blockchain:
truffle migrate -i 101
You may need to add the --reset flag if you are deploying a contract without any changes.
Ensure that there is a variable for each contract in the config.js file (follow the pattern of the paymentContractAddress export). Use this variable to instantiate contracts.
You can access the contract Owner address & key through the HD Wallet provider - defined in truffle.js, or by inputting the mnemonic directly into MetaMask.
If you will be running tests locally, we recommend using MetaMask to transfer ether from the accounts generated by the mnemonic to accounts generated for your users.
gulp lint
To fix some errors automatically:
gulp lint:fix
The api is currently staged on the Ropsten Network at https://tokenplay-staging.herokuapp.com/
The API is implemented using the JSON-RPC 2.0 specfication, to test the API, you can install a tool like Postman to create POST requests.
The header of the request must have Content-Type: application/json and all requests must use the POST method. Example body and response JSON are supplied below for each method.
- signUpUser
- login
- setAccount
- getAccount
- getAccountBalance
- getErrorLog
- getGames
- buyGame
- registerGame
- getOwnedGames
- verifyGameOwner
NOTE: In the JSONRPC 2.0 standard, method parameters can be supplied as an object, as seen below, or as an array. The order of the array is fixed, and methods must be supplied in the order listed in the chart for each method below.
All unsuccessful responses will follow the template below, providing additional information about the error.
{
"jsonrpc": "2.0",
"id": 1,
"error": {
"code": -32603,
"message": "Exception: incorrect password",
"data": {
"success": false,
"errors": [
"Exception: incorrect password"
],
"errfor": {}
}
}
}
This method creates the user in the database, it returns the username and email.
This will also generate a private Ethereum wallet which is accessible using the account methods.
parameters | Definition |
---|---|
username |
used for user login (unique, string) |
email |
stored for recovery (unique, string) |
password |
secure, used for login (string) |
{
"jsonrpc": "2.0",
"method": "signUpUser",
"params": {
"username": "UserMcUser",
"email": "UserMcUser@user.com",
"password": "TokenPlay123"
},
"id": 1
}
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"username": "UserMcUser",
"email": "usermcuser@user.com"
}
}
This method returns a JSON Web Token representing the user session for use in all methods which require authentication.
Parameters | Definition |
---|---|
username |
used for user login (unique, string) |
password |
secure, used for login (string) |
{
"jsonrpc": "2.0",
"method": "login",
"params": {
"username": "UserMcUser",
"password": "TokenPlay123"
},
"id": 1
}
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"token": "eyJhbGciOiJIUzI1"
}
}
This method returns an account object with the new first and last name, along with the associated ethereum wallet address for the account. This method will be used to add or update user details.
Parameters | Definition |
---|---|
jwt |
secure identifier issued at login (string) |
firstName |
the user's first name (string) |
lastName |
the user's last name (string) |
{
"jsonrpc": "2.0",
"method": "setAccount",
"params": {
"jwt": "eyJhbGciOiJIUzI1",
"firstName": "John",
"lastName": "Smithee"
},
"id": 1
}
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"firstName": "John",
"lastName": "Smithee",
"walletId": "0xc0E6ABd0Cc9f038df619308c858E26D6D10B4dbD"
}
}
This method returns an account object with the first and last name, a gamerId(currently a placeholder), and a wallet address.
Parameters | Definition |
---|---|
jwt |
secure identifier issued at login (string) |
{
"jsonrpc": "2.0",
"method": "getAccount",
"params": {
"jwt": "eyJhbGciOiJIUzI1NiI"
},
"id": 1
}
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"account": {
"_id": "5b2947677020ad2b2497d7bb",
"walletId": "0xc0E6ABd0Cc9f038df619308c858E26D6D10B4dbD",
"gamerId": "placeholder",
"firstName": "John",
"lastName": "Smithee"
}
}
}
This returns an object with the balance for the account linked to the token in wei as a string.
Parameters | Definition |
---|---|
jwt |
secure identifier issued at login (string) |
{
"jsonrpc": "2.0",
"method": "getAccountBalance",
"params": {
"jwt": "eyJhbGciOiJIUzI1NiIs"
},
"id": 1
}
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"balance": "102937800000000000"
}
}
This method returns the logged errors associated with the user linked to the supplied jwt, sorted by most recent. The response will be paginated.
Parameters | Definition |
---|---|
jwt |
secure identifier issued at login (string) |
page |
skips an amount of results equal to limit (starts at 1, integer) |
limit |
maximum number of results per page (integer) |
{
"jsonrpc": "2.0",
"method": "getErrorLog",
"params": {
"jwt": "eyJhbGciOiJIUzI1Ni",
"page": 1,
"limit": 20
},
"id": 1
}
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"data": [
{
"_id": "5b22ca4114bada1c44663315",
"user": "5b20af3900427210f2de9247",
"message": "insufficient funds for gas * price + value",
"timeStamp": 1529006657615,
"__v": 0
},
{
"_id": "5b22ca4114bada1c44663314",
"user": "5b20af3900427210f2de9247",
"message": "insufficient funds for gas * price + value",
"timeStamp": 1529006657614,
"__v": 0
}
],
"pages": {
"current": 10,
"prev": 9,
"hasPrev": true,
"next": 11,
"hasNext": true,
"total": 12
},
"items": {
"begin": 19,
"end": 20,
"total": 24
}
}
}
This method returns a paginated list of registered games.
Parameters | Definition |
---|---|
page |
(integer) skips an amount of results equal to limit (starts at 1) |
limit |
maximum number of results per page (integer) |
{
"jsonrpc": "2.0",
"method": "getGames",
"params": {
"page": 2,
"limit": 20
},
"id": 1
}
{
"jsonrpc":"2.0",
"id":1,
"result":{
"data":[
{
"searchKeyWords":"",
"externalLinks":[
{
"gameURL":"http://tueborgame.com",
"forumURL":"",
"statsURL":"",
"manualURL":""
}
],
"platformSpecs":[
{
"platform":"Windows",
"minimumSpecs":[
{
"name":"OS Version",
"description":"Windows 8 x64"
}
],
"recommendedSpecs":[
{
"name":"OS Version",
"description":"Windows 10 x64"
}
]
}
],
"supportedLanguages":[
{
"name":"English",
"supports":[
"Interface",
"Full Audio",
"Subtitles"
]
}
],
"genres":[
"Action",
"Casual",
"Indie",
"Sports",
"Early Access"
],
"players":[
"Multi-player"
],
"virtualReality":[
""
],
"legalNotice":"",
"agencyRatings":[
{
"rating":"",
"discriptor":""
}
],
"googleAnalyticsID":"",
"downloadableContent":"",
"associatedDemos":"",
"platforms":[
"Windows"
],
"_id":"5b1cafffdce6860004970de5",
"name":"Tuebor: I Will Defend",
"releaseDate":1525995996,
"support":{
"url":"",
"email":"",
"phone":""
},
"drm":{
"provider":"",
"limit":""
},
"publisher":{
"name":"Strength in Numbers Studios, Inc"
},
"images":{
"teaser":{
"url":"https://tokenplay.s3.amazonaws.com/media/8/teaser.png"
},
"cover":{
"url":"https://tokenplay.s3.amazonaws.com/media/8/cover.png"
}
},
"medias":[
{
"id":1,
"type":"video",
"previewUrl":"https://tokenplay.s3.amazonaws.com/media/8/preview.png",
"title":"Tuebor: I Will Defend",
"src":"https://tokenplay.s3.amazonaws.com/media/8/1.mp4"
}
],
"price":"41942197307911780",
"package":{
"id":"tue",
"defaultDest":"C:\\Tokenplay\\games\\tue",
"installSize":0,
"downloaded":false
},
"rating":{
"numerator":4.2,
"denominator":5,
"populationSize":8502306
},
"tokensEarned":38501069,
"createdBy":"5b17f85912c0db00042c96c6",
"gameId":"8",
"__v":0
},
{
"searchKeyWords":"",
"externalLinks":[
{
"gameURL":"https://avariavs.com/",
"forumURL":"",
"statsURL":"",
"manualURL":""
}
],
"platformSpecs":[
{
"platform":"Windows",
"minimumSpecs":[
{
"name":"OS Version",
"description":"Windows 8 x64"
}
],
"recommendedSpecs":[
{
"name":"OS Version",
"description":"Windows 10 x64"
}
]
}
],
"supportedLanguages":[
{
"name":"English",
"supports":[
"Interface",
"Full Audio",
"Subtitles"
]
}
],
"genres":[
"Action",
"Indie",
"Strategy"
],
"players":[
"Multi-player"
],
"virtualReality":[
""
],
"legalNotice":"",
"agencyRatings":[
{
"rating":"",
"discriptor":""
}
],
"googleAnalyticsID":"",
"downloadableContent":"",
"associatedDemos":"",
"platforms":[
"Windows"
],
"_id":"5b1cb02fdce6860004970de6",
"name":"AVARIAvs",
"releaseDate":1525995996,
"support":{
"url":"",
"email":"",
"phone":""
},
"drm":{
"provider":"",
"limit":""
},
"publisher":{
"name":"Juncture Media"
},
"images":{
"teaser":{
"url":"https://tokenplay.s3.amazonaws.com/media/9/teaser.png"
},
"cover":{
"url":"https://tokenplay.s3.amazonaws.com/media/9/cover.png"
}
},
"medias":[
{
"id":1,
"type":"video",
"previewUrl":"https://tokenplay.s3.amazonaws.com/media/9/preview.png",
"title":"AVARIAvs",
"src":"https://tokenplay.s3.amazonaws.com/media/9/1.mp4"
}
],
"price":"41942197307911780",
"package":{
"id":"ava",
"defaultDest":"C:\\Tokenplay\\games\\ava",
"installSize":2255855616,
"downloaded":false
},
"rating":{
"numerator":4.2,
"denominator":5,
"populationSize":8502306
},
"tokensEarned":38501069,
"createdBy":"5b17f85912c0db00042c96c6",
"gameId":"9",
"__v":0
}
],
"pages":{
"current":2,
"prev":1,
"hasPrev":true,
"next":3,
"hasNext":true,
"total":10
},
"items":{
"begin":3,
"end":4,
"total":19
}
}
}
This method invokes the smart contract, and saves a token on success, it returns a success object at the beginning of the Ethereum transaction.
Parameters | Definition |
---|---|
jwt |
secure identifier issued at login (string) |
gameId |
unique identifier for a game (integer) |
{
"jsonrpc": "2.0",
"method": "buyGame",
"params": {
"jwt": "eyJhbGciOiJIUzI1NiIs",
"gameId": "7"
},
"id": 1
}
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"message": "Game order processing."
}
}
This method invokes the smart contract to register the game, it returns a success object at the beginning of the Ethereum transaction.
The game Id must be an unsigned integer.
Parameters | Definition |
---|---|
jwt |
secure identifier issued at login (string) |
gameObj |
object describing the game (requires id, name, price, and publisher, see below for full options) |
{
"jsonrpc": "2.0",
"method": "registerGame",
"params": {
"jwt": "eyJhbGciOiJIUzI1NiIs",
"gameObj": {
"id": "18",
"name": "Outbreak: Pandemic Evolution",
"searchKeywords": "Action, Adventure, Indie, Early Access",
"externalLinks": {
"gameURL": "",
"forumURL": "",
"statsURL": "",
"manualURL": ""
},
"platformSpecs": [
{
"platform": "Windows",
"minimumSpecs": [
{
"name": "OS Version",
"description": "Windows 8 x64"
}
],
"recommendedSpecs": [
{
"name": "OS Version",
"description": "Windows 10 x64"
}
]
}
],
"releaseDate": 1525995996,
"supportedLanguages": [
{
"name": "English",
"supports": [
"Interface",
"Full Audio",
"Subtitles"
]
}
],
"genres": [
"Action",
"Adventure",
"Indie",
"Early Access"
],
"players": [
"multi-player"
],
"virtualReality": [
""
],
"legalNotice": "",
"support": {
"url": "",
"email": "",
"phone": ""
},
"drm": {
"provider": "",
"limit": ""
},
"agencyRatings": [
{
"rating": "",
"discriptor": ""
}
],
"googleAnalyticsID": "",
"downloadableContent": "",
"associatedDemos": "",
"publisher": {
"name": "Forthright Entertainment"
},
"platforms": [
"Windows"
],
"images": {
"teaser": {
"url": "https://tokenplay.s3.amazonaws.com/media/18/teaser.png"
},
"cover": {
"url": "https://tokenplay.s3.amazonaws.com/media/18/cover.png"
}
},
"medias": [
{
"id": 1,
"type": "video",
"previewUrl": "https://tokenplay.s3.amazonaws.com/media/18/preview.png",
"title": "Outbreak: Pandemic Evolution",
"src": "https://tokenplay.s3.amazonaws.com/media/18/1.mp4"
}
],
"price": 0.0483,
"package": {
"id": "out",
"defaultDest": "C:\\Tokenplay\\games\\out",
"installSize": 6751690752,
"downloaded": false
},
"rating": {
"numerator": 4.2,
"denominator": 5,
"populationSize": 8502306
},
"tokensEarned": 38501069,
"description": ""
}
},
"id": 1
}
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"message": "Game registration processed!"
}
}
This method returns a list of token owned by the user linked to the supplied jwt user along with information about the game associated to the token, each game token will have a status. a status of 'processing' indicates that the purchase transaction is not complete. a status of 'confirmed' indicates that the purchase is complete, and you will see a tokenId with any confirmed transaction, a status of 'failed' indicates that the transaction has failed. The tokenId property is not available for failed or processing transactions. the response also contains a summary about the number of items.
Parameters | Definition |
---|---|
jwt |
secure identifier issued at login (string) |
page |
skips an amount of results equal to limit (starts at 1, integer) |
limit |
maximum number of results per page (integer) |
{
"jsonrpc": "2.0",
"method": "getOwnedGames",
"params": {
"jwt": "eyJhbGciOiJIUzI1NiIs",
"page": 1,
"limit": 20
},
"id": 1
}
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"data": [
{
"_id": "5b242ce512a7a0226850fdbe",
"user": "5b1ec95f4fdba115688ca244",
"game": {
"searchKeyWords": "",
"externalLinks": [
{
"gameURL": "http://www.heavygear.com",
"forumURL": "",
"statsURL": "",
"manualURL": ""
}
],
"platformSpecs": [
{
"platform": "Windows",
"minimumSpecs": [
{
"name": "OS Version",
"description": "Windows 8 x64"
}
],
"recommendedSpecs": [
{
"name": "OS Version",
"description": "Windows 10 x64"
}
]
}
],
"supportedLanguages": [
{
"name": "English",
"supports": [
"Interface",
"Full Audio",
"Subtitles"
]
}
],
"genres": [
"Action",
"Indie",
"Simulation",
"Early Access"
],
"players": [
"Multi-player"
],
"virtualReality": [
""
],
"legalNotice": "",
"agencyRatings": [
{
"rating": "",
"discriptor": ""
}
],
"googleAnalyticsID": "",
"downloadableContent": "",
"associatedDemos": "",
"platforms": [
"Windows"
],
"_id": "5b242cd712a7a0226850fdab",
"name": "Heavy Gear Assault",
"releaseDate": 1525995996,
"support": {
"url": "",
"email": "",
"phone": ""
},
"drm": {
"provider": "",
"limit": ""
},
"publisher": {
"name": "Stompy Bot Productions"
},
"images": {
"teaser": {
"url": "https://tokenplay.s3.amazonaws.com/media/7/te aser.png"
},
"cover": {
"url": "https://tokenplay.s3.amazonaws.com/media/7/co ver.png"
}
},
"medias": [
{
"id": 1,
"type": "video",
"previewUrl": "https://tokenplay.s3.amazonaws.com/media/7/pr eview.png",
"title": "Heavy Gear Assault",
"src": "https://tokenplay.s3.amazonaws.com/media/7/1. mp4"
}
],
"price": "18800000000000000",
"package": {
"id": "hga",
"defaultDest": "C:\\Tokenplay\\games\\hga",
"installSize": 4001599488,
"downloaded": false
},
"rating": {
"numerator": 4.2,
"denominator": 5,
"populationSize": 8502306
},
"tokensEarned": 38501069,
"createdBy": "5b1ec95f4fdba115688ca244",
"gameId": "7",
"__v": 0
},
"status": "confirmed",
"__v": 0,
"orderId": "1",
"tokenId": "1",
"tokenIssueDate": "2018-06-15T21:17:26.420Z"
}
],
"pages": {
"current": 1,
"prev": 0,
"hasPrev": false,
"next": 2,
"hasNext": true,
"total": 3
},
"items": {
"begin": 1,
"end": 1,
"total": 3
}
}
}
This method returns true or false depending on whether that user or ethereum address owns the tokenId supplied.
Parameters | Definition |
---|---|
username |
secure identifier issued at login OR ethereum address (string) |
tokenId |
unique identifier of the token being checked (integer) |
{
"jsonrpc": "2.0",
"method": "verifyGameOwner",
"params": {
"username": "0x231e499039D3298A6fbF0b5a06e16fB7C54B7917",
"tokenId": 3
},
"id": 1
}
{
"jsonrpc": "2.0",
"id": 1,
"result": true
}
This method returns the Ethereum address of the customer, and the gameId purchased, this method specifically draws information from the smart contract.
Parameters | Definition |
---|---|
jwt |
secure identifier issued at login (string) |
orderId |
unique identifier of the order being checked (integer) |
"jsonrpc": "2.0",
"method": "getGameOrder",
"params": {
"jwt": "eyJhbGciOiJIUzI1NiIs",
"orderId": 1
},
"id": 1
}
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"customerAddress": "0x3c7a5a523eC4b22c72B74f3a1763776d5779A56A",
"gameId": "7"
}
}
This method can be used to complete a game order where the order succeeded, but the minting of a token failed for any reason. It invokes the smart contract and returns a success message when the transaction has started.
Parameters | Definition |
---|---|
jwt |
secure identifier issued at login (string) |
orderId |
the unique identifier assigned to the transaction (uint, can be acquired using getGameOrder method) |
{ "jsonrpc": "2.0", "method": "acceptGameOrder", "params": { "jwt": "eyJhbGciOiJIUzI1NiI", "orderId": "42" }, "id": 1 }
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"message": "Processing order acceptance"
}
}
This method invokes the smart contract and initiates a transaction to set the price, it returns a success object at the beginning of the transaction.
Parameters | Definition |
---|---|
jwt |
secure identifier issued at login (string) |
gameId |
unique identifier of the game being changed (integer) |
price |
price of the game in Ether. (number) can also be in Wei (string) |
{
"jsonrpc": "2.0",
"method": "setPrice",
"params": {
"jwt": "eyJhbGciOiJIUzI1NiIsI",
"gameId": 2,
"price": 0.001
},
"id": 1
}
{
"jsonrpc": "2.0",
"id": 1,
"result": "updating price"
}
This method allows an authorized user to transfer funds out of the ethereum wallet we create for them, the funds are transferred to the specified ethereum address, it returns a success object at the beginning of the transaction.
The minimum balance to perform this action must include the amount, plus the gas price times the gas limit.
Parameters | Definition |
---|---|
jwt |
secure identifier issued at login (string) |
destAddress |
the address the funds will be transferred to (string) |
amount |
the amount being transferred in wei (string) |
{
"jsonrpc": "2.0",
"method": "transferFunds",
"params": {
"jwt": "eyJhbGciOiJIUzI1NiIsI",
"destAddress": "0xbb3b1433b771339af429ccb02c17e4ddf8c492ff",
"amount": "1000000000000000000"
},
"id": 1
}
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"message": "Sending Transaction!"
}
}