Skip to content

Commit

Permalink
Explorer rebuilt for Flask; initial Creator tool
Browse files Browse the repository at this point in the history
  • Loading branch information
kdmukai committed Jul 20, 2019
1 parent 3c305c1 commit 46cb81d
Show file tree
Hide file tree
Showing 33 changed files with 1,077 additions and 248 deletions.
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
*.o
*.obj
*.abi
__pycache__
*.pyc

# Precompiled Headers
*.gch
Expand Down Expand Up @@ -39,3 +41,5 @@ node_modules/


wallet_password.txt
local_settings.js
zappa_settings.json
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -321,11 +321,11 @@ cleos -u https://chain.wax.io set contract waxbadgesftw /path/to/contracts/waxba
cleos -u https://chain.wax.io push action waxbadgesftw wipetables '[]' -p waxbadgesftw@active
cleos -u https://chain.wax.io push action waxbadgesftw addecosys '["waxbadgesftw", "WAXBadges Genesis Campaign", "https://waxbadges.com", "assets.waxbadges.com/ecosys/genesis", "waxbadges_logo.png"]' -p waxbadgesftw@active
cleos -u https://chain.wax.io push action waxbadgesftw addecosys '["waxbadgesftw", "WAXBadges Genesis Campaign", "https://waxbadges.com", "explorer.waxbadges.com/assets", "waxbadges_logo.png"]' -p waxbadgesftw@active
cleos -u https://chain.wax.io push action waxbadgesftw addcat '["waxbadgesftw", "0", "twitter"]' -p waxbadgesftw@active
cleos -u https://chain.wax.io push action waxbadgesftw addach '["waxbadgesftw", "0", "0", "First", "First achievement ever. First 50 to follow @WAXBadges.", "first.png", "50"]' -p waxbadgesftw@active
cleos -u https://chain.wax.io push action waxbadgesftw addach '["waxbadgesftw", "0", "0", "First", "First achievement ever. First 50 to follow @WAXBadges.", "ach_hand.png", "50"]' -p waxbadgesftw@active
```


Expand All @@ -347,4 +347,4 @@ cleos -u https://chain.wax.io push action waxbadgesftw addach '["waxbadgesftw",

* Add support for a points system for each `Achievement`, point totals for `User`s?

*
* Shard Ecosystems User table for huge ecosystems?
58 changes: 58 additions & 0 deletions creator/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# WAXBadges Achievements CREATOR
_A tool for game studios (and anyone else) to create their own WAXBadges achievement ecosystem_


This creator is built as static html/JS that you run locally to create and management your WAXBadges achievements. By running locally we can make some safe-ish security compromises that greatly enhance your workflow speed (i.e. hard-coding your private key for instant transaction signing; normally a MEGA no-no!!).


### Getting started:
* Set up your WAX blockchain account
* Create a free WAX All Access account ([account.wax.io](https://account.wax.io))
* On the Scatter step, opt to generate new keys that are stored in Scatter. We'll need direct access to your private key.
* Fund it with some WAX tokens
* Buy from an exchange and transfer to your WAX account (details are beyond the scope of this doc)
* Purchase some RAM
* Spend WAX to buy RAM in Scatter (how much? A couple hundred kB is fine for starters)
* Download the static html/js files
* Optionally build it from source yourself
* Customize the `local_settings.js` file
* Enter your account's private key
* Enter the associated WAX account name (e.g. abc12.waa)
* Double click the `index.html` to run it as a locally-hosted webpage

Now that you've hard-coded your private key you must keep these files secure on your local machine. Never upload or host these files on the web! The creator looks and acts like a normal website, but treat it as a tool that you install on your local machine. If anyone else on your team needs access, they'll have to download and customize their own local copy.


### Wait, isn't this risky?
I mean, kinda. We're greatly reducing risk by only running this customized javascript on your local computer. But anytime you're directly handling a private key you are absolutely in a danger zone.

A future enhancement will enable an _authorize-in-Scatter_ option. This will keep your private key in Scatter (or, even better, in a hardware wallet that's connected to Scatter). But as a trade-off you'll have to manually approve every transaction. If you're adding 30 achievements to your new ecosystem it's going to be a pain, though hopefully we'll be able to batch transactions to reduce the number of approvals.


### Bonus security
Add a hardware wallet like a Ledger to Scatter and change your WAX account's "owner" key to the hardware wallet key. The steps above risk compromising your WAX account's "active" key but if you retain tight control of the "owner" key, you can always generate a new "active" key for your account.



## Building from source
Grab the `npm` modules:
```
npm init
npm install
```

Build for dev:
```
npm run dev
```

Run locally with Hot Module Replacement:
```
webpack-dev-server --hot --host 0.0.0.0
```

Build minified production payload:
```
npm run build
```

File renamed without changes.
7 changes: 1 addition & 6 deletions explorer/package-lock.json → creator/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion explorer/package.json → creator/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
"author": "",
"license": "ISC",
"dependencies": {
"bootstrap": "^4.3.1",
"eosjs": "^20.0.0",
"jquery": "^3.4.1"
},
Expand Down
189 changes: 189 additions & 0 deletions creator/src/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
let $ = require("jquery");

import { Api, JsonRpc, RpcError } from 'eosjs';
import { JsSignatureProvider } from 'eosjs/dist/eosjs-jssig';

let PageOverlay = require("./pageoverlay");
var pageoverlay;


var rpc = new JsonRpc('https://chain.wax.io');
var api;
var signatureProvider;


let CONTRACT = 'waxbadgesftw';


async function renderIndex() {
console.log(ACCOUNT_NAME);

pageoverlay.initPageOverlay($("#add_ecosys_container"));
$("#add_ecosys_button").click(function() {
console.log("click");
pageoverlay.showPageOverlay($("#add_ecosys_container"));
});

$("#add_ecosys_submit_button").click(function() {
let name = $("#add_ecosys_name").val();
let description = $("#add_ecosys_description").val();
let website = $("#add_ecosys_website").val();
let assetbaseurl = $("#add_ecosys_assetbaseurl").val();
let logoassetname = $("#add_ecosys_logoassetname").val();

if (name == "") {
alert("ecosystem name is required!");
return;
}
if (website != "" && (!website.startsWith('http://') && !website.startsWith('https://'))) {
alert("website should begin with 'http' or 'https'");
return;
}
if (website == "" && assetbaseurl != "") {
alert("website is required if assetbaseurl is specified!");
return;
}

(async () => {
const result = await api.transact(
{
actions: [{
account: CONTRACT,
name: 'addecosys',
authorization: [{
actor: ACCOUNT_NAME,
permission: 'active',
}],
data: {
ecosystem_owner: ACCOUNT_NAME,
ecosystem_name: name,
description: description,
website: website,
assetbaseurl: assetbaseurl,
logoassetname: logoassetname
},
}]
},
{
blocksBehind: 3,
expireSeconds: 30,
}
);

console.dir(result);
})();

});


const resp = await rpc.get_table_rows({
json: true, // Get the response as json
code: CONTRACT, // Contract that we target
scope: CONTRACT, // Account that owns the data
table: 'ecosystems', // Table name
index_position: 2, // Table secondary key index
key_type: 'i64', // Secondary index type
lower_bound: ACCOUNT_NAME, // Table secondary key value
upper_bound: ACCOUNT_NAME, // Table secondary key value
limit: 10, // Maximum number of rows that we want to get
});

console.log(resp.rows);

for(var i=0; i < resp.rows.length; i++) {
var ecosystem = resp.rows[i];
$("#page_index").find(".ecosystems_list").append("<div><a href='/ecosys/" + ecosystem.key + "'>" + ecosystem.name + "</a></div>");
}

$("#page_index").show();
}



// ecosys.html
async function renderEcosys(key) {
const resp = await rpc.get_table_rows({
json: true, // Get the response as json
code: CONTRACT, // Contract that we target
scope: CONTRACT, // Account that owns the data
table: 'ecosystems', // Table name
lower_bound: key,
upper_bound: key,
limit: 1, // Maximum number of rows that we want to get
});

console.log(resp.rows);

if (resp.rows.length == 0) {
window.location.href = "/";
return;
}

var ecosystem = resp.rows[0];
document.title = ecosystem.name + " | WAXBadges Achievements Explorer"
$("meta[property='og:title']").attr("content", ecosystem.name + " | WAXBadges Achievements Explorer");
$("meta[property='og:description']").attr("content", ecosystem.description);
$("meta[name='twitter:card'][content='summary']").text(ecosystem.description);
$("#ecosystem_name").text(ecosystem.name);
$("#ecosystem_website").append("<a href='" + ecosystem.website + "' target='_new'>" + ecosystem.website + "</a>");
$("#ecosystem_description").text(ecosystem.description);

for (var i=0; i < ecosystem.categories.length; i++) {
let category = ecosystem.categories[i];
console.log(category);

let $cat_template = $(".category_template").clone().appendTo("#category_list");
$cat_template.attr("id", "cat_" + i);
$cat_template.find(".cat_name").text(category.name);
$cat_template.show();

for (var j=0; j < category.achievements.length; j++) {
let achievement = category.achievements[j];
console.log(achievement);
let $ach_template = $(".achievement_template").first().clone();
$ach_template.insertBefore($cat_template.find('.ach_list .clearfix'));
$ach_template = $ach_template.attr("id", "cat_" + i + "_ach_" + j);
$ach_template.find(".ach_name").text(achievement.name);
$ach_template.find(".ach_asset").attr("src", "//" + ecosystem.assetbaseurl + "/" + achievement.assetname);

if (achievement.maxquantity != "0") {
$ach_template.find(".ach_quantity").text("max: " + achievement.maxquantity);
} else {
$ach_template.find(".ach_quantity").text("unlimited");
}

$ach_template.find(".ach_description").text(achievement.description);
$ach_template.show();
}
}

$("#ecosystem_container").show();
}



$(document).ready(function() {

signatureProvider = new JsSignatureProvider([PRIVATE_KEY]);
api = new Api({ rpc, signatureProvider });

$(".account_name").text(ACCOUNT_NAME);

pageoverlay = new PageOverlay();

let pathParts = window.location.pathname.split('/', 3);
let page = pathParts[1];
var param;
if (page != "") {
param = parseInt(pathParts[2]);
}
if (page == 'ecosys') {
renderEcosys(param);
} else {
renderIndex();
}

});



87 changes: 87 additions & 0 deletions creator/src/pageoverlay.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
let $ = require("jquery");


class PageOverlay {
constructor() {
const style = `
<style>
.page_overlay {
display: none;
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0,0,0, 0.75);
text-align: center;
z-index: 20;
}
.page_overlay_popup {
display: none;
opacity: 0.0;
margin: 0 auto;
max-width: 650px;
text-align: center;
border-radius: 3em;
border: 5px solid #6cc7cc;
background-color: #eee;
padding: 0.5em;
padding-bottom: 1.5em;
z-index: 30;
}
</style>
`;
const content = `
<div class="page_overlay" id="page_overlay">
<div class="page_overlay_popup" id="page_overlay_popup">
<div id="page_overlay_popup_content"></div>
<!--div class="float_right"><div id="page_overlay_popup_cancel_button" class="stylized_button stylized_button_tiny">cancel</div></div-->
<br/>
<div class="clear-both"></div>
</div>
</div>
`;
document.head.insertAdjacentHTML('beforeend', style);
document.body.insertAdjacentHTML('beforeend', content);
}

initPageOverlay(content_selector) {
// Transfer the content to a new hidden div
var $holder = $("<div/>").hide().appendTo("body");
$(content_selector).appendTo($holder);

// Make sure the content will be visible, even though it'll still be hidden in its parent div.
$(content_selector).show();
}


showPageOverlay(content_selector) {
// Transfer the content to the popup overlay div
$(content_selector).appendTo($("#page_overlay_popup_content"));
$("#page_overlay").show();

var top_position = (window.innerHeight/2) - $("#page_overlay_popup").height();
if (top_position < 20) { top_position = 20; }
$("#page_overlay_popup").css({
"position": "relative",
"display": "block",
"top": top_position,
});

window.scrollTo(0, 0);
$("#page_overlay_popup").animate({opacity:1.0}, 500);
}


hidePageOverlay() {
// Transfer the content back into a holder div
this.initPageOverlay($("#page_overlay_popup_content").children());

$("#page_overlay").hide();
$("#page_overlay_popup").css({opacity:0.0});
}

}


module.exports = PageOverlay
Loading

0 comments on commit 46cb81d

Please sign in to comment.