Skip to content

Instantly share code, notes, and snippets.

@okwme
Created May 7, 2020 09:51
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save okwme/01583a547b1a2550a9d3eee94cd4699c to your computer and use it in GitHub Desktop.
Save okwme/01583a547b1a2550a9d3eee94cd4699c to your computer and use it in GitHub Desktop.
var BALANCES_SHEET = SpreadsheetApp.getActiveSpreadsheet().getSheets()[0];
var WITHDRAWALS_SHEET = SpreadsheetApp.getActiveSpreadsheet().getSheets()[1];
var DEPOSITS_SHEET = SpreadsheetApp.getActiveSpreadsheet().getSheets()[2];
var OWNERS_SHEET = SpreadsheetApp.getActiveSpreadsheet().getSheets()[4];
// Use this code for Google Docs, Forms, or new Sheets.
function onOpen() {
SpreadsheetApp.getUi() // Or DocumentApp or SlidesApp or FormApp.
.createMenu('Sheetcoin')
.addItem('View Balance', 'viewBalance')
.addItem('Send Coins', 'sendCoinsPrompt')
.addItem('Withdraw Coins', 'withdrawCoinsPrompt')
.addItem('Deposit Coins', 'depositCoinsPrompt')
.addSubMenu(SpreadsheetApp.getUi().createMenu('Owners Only')
.addItem('Add Owner', 'addOwnerPrompt')
.addItem('Remove Owner', 'removeOwnerPrompt'))
//.addItem('Mint Coins', 'mintCoinsPrompt')
.addToUi();
}
/**
* Displays an HTML-service dialog in Google Sheets that contains client-side
* JavaScript code for the Google Picker API.
*/
function sendTxBox(txData) {
var htmlTemplate = HtmlService.createTemplateFromFile('picker.html');
htmlTemplate.dataFromServerTemplate = { nonce: txData };
var html = htmlTemplate.evaluate().setSandboxMode(HtmlService.SandboxMode.IFRAME)
.setWidth(200)
.setHeight(125);
SpreadsheetApp.getUi().showModalDialog(html, 'Sign Tx');
}
function broadcastSignedToken(token) {
var options = {
'method' : 'post',
'payload' : {
'token' : token,
}
};
var response = UrlFetchApp.fetch('https://script.google.com/macros/s/AKfycby2aArgZZ9x7kZhdJdgTuLZ5uqeA4kXQ6ZtNI1DR5_P4t5vJqE/exec', options);
Logger.log(response.getContentText());
var res = JSON.parse(response.getContentText())
if (res.Error) {
SpreadsheetApp.getUi().alert("Tx Failed. Error: " + res.Message);
} else {
Logger.log(res)
if (res.TxData.action == "send") {
newBalance = BALANCES_SHEET.getRange(getRowOfAddress(Session.getActiveUser().getEmail()), 2).getValue()
SpreadsheetApp.getUi().alert("Send Transaction successful! Your new balance: " + newBalance);
} else if (res.TxData.action == "withdraw_request") {
var result = SpreadsheetApp.getUi().alert("Withdraw Request Successful!", SpreadsheetApp.getUi().ButtonSet.OK);
Logger.log(result)
if (result != "OK") {
Logger.log("wtf");
return;
}
var result = SpreadsheetApp.getUi().alert("Would you like to relay to Ethereum?", SpreadsheetApp.getUi().ButtonSet.YES_NO)
Logger.log(result)
Logger.log("whyme");
if (result != "YES") {
Logger.log("why");
return;
}
Logger.log("asdf")
Logger.log(res);
openUrl(res.RelayURL);
return;
}
}
}
/**
* Open a URL in a new tab.
*/
function openUrl( url ){
var html = HtmlService.createHtmlOutput('<html><script>'
+'window.close = function(){window.setTimeout(function(){google.script.host.close()},9)};'
+'var a = document.createElement("a"); a.href="'+url+'"; a.target="_blank";'
+'if(document.createEvent){'
+' var event=document.createEvent("MouseEvents");'
+' if(navigator.userAgent.toLowerCase().indexOf("firefox")>-1){window.document.body.append(a)}'
+' event.initEvent("click",true,true); a.dispatchEvent(event);'
+'}else{ a.click() }'
+'close();'
+'</script>'
// Offer URL as clickable link in case above code fails.
+'<body style="word-break:break-word;font-family:sans-serif;">Failed to open automatically. <a href="'+url+'" target="_blank" onclick="window.close()">Click here to proceed</a>.</body>'
+'<script>google.script.host.setHeight(40);google.script.host.setWidth(410)</script>'
+'</html>')
.setWidth( 90 ).setHeight( 1 );
SpreadsheetApp.getUi().showModalDialog( html, "Opening Page ..." );
}
function verifyValidEmailAddress(input) {
var re = /\S+@\S+\.\S+/;
return re.test(input);
}
function verifyValidEthereumAddress(address) {
// check if it has the basic requirements of an address
if (!/^(0x)?[0-9a-f]{40}$/i.test(address)) {
return false;
// If it's ALL lowercase or ALL upppercase
} else if (/^(0x|0X)?[0-9A-Fa-f]{40}$/.test(address)) {
return true;
// Otherwise check each case
} else {
return false;
}
}
function viewBalance() {
ui = SpreadsheetApp.getUi()
balance = BALANCES_SHEET.getRange(getRowOfAddress(Session.getActiveUser().getEmail()), 2).getValue()
ui.alert("View Balance", "Your current balance is: " + balance, ui.ButtonSet.OK)
}
function addOwnerPrompt() {
ui = SpreadsheetApp.getUi()
row = getRowOfOwner(Session.getActiveUser().getEmail())
if (row == undefined) {
ui.alert("Action Denied", "Sorry, you are not an owner. Only owners can add and remove owners", ui.ButtonSet.OK);
}
var result = ui.prompt(
'Who would you like to add as an owner?',
'Please enter their address:',
ui.ButtonSet.OK_CANCEL);
// Process the user's response.
var button = result.getSelectedButton();
var newOwner = result.getResponseText();
if (button != ui.Button.OK) {
return;
}
// Validate Email Address
if (!verifyValidEmailAddress(newOwner)) {
ui.alert("Invalid Address");
return;
}
row = getRowOfOwner(newOwner)
if (row != undefined) {
ui.alert("" + newOwner + "is already an owner.");
return;
}
OWNERS_SHEET.appendRow([newOwner]);
var ss = SpreadsheetApp.getActive();
var protections = ss.getProtections(SpreadsheetApp.ProtectionType.SHEET);
Logger.log(protections)
for (var i = 0; i < protections.length; i++) {
Logger.log(i)
Logger.log(protections[i])
var protection = protections[i];
protection.addEditor(newOwner);
}
}
function removeOwnerPrompt() {
ui = SpreadsheetApp.getUi()
row = getRowOfOwner(Session.getActiveUser().getEmail())
if (row == undefined) {
ui.alert("Action Denied", "Sorry, you are not an owner. Only owners can add and remove owners", ui.ButtonSet.OK);
}
var result = ui.prompt(
'Who would you like to remove as an owner?',
'Please enter their address:',
ui.ButtonSet.OK_CANCEL);
// Process the user's response.
var button = result.getSelectedButton();
var exOwner = result.getResponseText();
if (button != ui.Button.OK) {
return;
}
// Validate Email Address
if (!verifyValidEmailAddress(exOwner)) {
ui.alert("Invalid Address");
return;
}
row = getRowOfOwner(exOwner)
if (row == undefined) {
ui.alert("" + exOwner + "is not an owner.");
return;
}
delCellAndShift(OWNERS_SHEET, row, 1, "up")
var ss = SpreadsheetApp.getActive();
var protections = ss.getProtections(SpreadsheetApp.ProtectionType.SHEET);
Logger.log(protections)
for (var i = 0; i < protections.length; i++) {
Logger.log(i)
Logger.log(protections[i])
var protection = protections[i];
protection.removeEditor(exOwner);
}
}
function delCellAndShift(sh,row,column,shift)
{
if(row && column)
{
var shift=(typeof(shift)!='undefined')?shift:'left';
var ss=SpreadsheetApp.getActive();
var rg=sh.getDataRange()
var vA=rg.getValues();
if(shift=='left')
{
var tA=vA[row-1];
tA.splice(column-1,1);
tA.push('');
vA[row-1]=tA;
rg.setValues(vA);
}
if(shift=='up')
{
var vB=Object.keys(vA[0]).map(function (c) { return vA.map(function (r) { return r[c]; }); });
var tA=vB[column-1];
tA.splice(row-1,1);
tA.push('');
vB[column-1]=tA;
vA=Object.keys(vB[0]).map(function (c) { return vB.map(function (r) { return r[c]; }); });
rg.setValues(vA);
}
}
else
{
var s='Invalid Inputs for delCellAndShift.<br /><input type="button" value="Close" onClick="google.script.host.close();" />';
var ui=HtmlService.createHtmlOutput(s);
SpreadsheetApp.getUi().showModalDialog(us, 'Invalid Inputs')
}
}
function mintCoinsPrompt() {
var ui = SpreadsheetApp.getUi(); // Same variations.
var result = ui.prompt(
'Who are you minting coins to?',
'Please enter their address:',
ui.ButtonSet.OK_CANCEL);
// Process the user's response.
var button = result.getSelectedButton();
var addressInput = result.getResponseText();
if (button == ui.Button.OK) {
// User clicked "OK".
// Validate Email Address
if (!verifyValidEmailAddress(addressInput)) {
ui.alert("Invalid Address");
return;
}
row = getRowOfAddress(addressInput);
if (row === undefined) {
var result = ui.alert(
'This address does not already have an account. A new account will be created.',
'Are you sure you want to continue?',
ui.ButtonSet.YES_NO);
// Process the user's response.
if (result == ui.Button.YES) {
// User clicked "Yes".
} else {
// User clicked "No" or X in the title bar.
ui.alert("TX Cancelled");
return;
}
}
var result = ui.prompt(
'How much would you like to mint?',
'Please enter an amount:',
ui.ButtonSet.OK_CANCEL);
var button = result.getSelectedButton();
var amountInput = result.getResponseText();
if (button == ui.Button.OK) {
if (!isNaN(amountInput)){
amount = +amountInput
} else {
ui.alert("Must input a valid number.");
return;
}
} else {
// User clicked X in the title bar.
ui.alert("TX Cancelled");
return;
}
} else {
// User clicked X in the title bar.
ui.alert("TX Cancelled");
return;
}
mint(addressInput, amount)
}
function sendCoinsPrompt() {
var ui = SpreadsheetApp.getUi(); // Same variations.
fromAddress = Session.getActiveUser().getEmail();
fromRow = getRowOfAddress(fromAddress);
if (fromRow===undefined) {
ui.alert("You don't have any coins to send yet.");
}
senderBalance = BALANCES_SHEET.getRange(fromRow, 2).getValue()
var result = ui.prompt(
'Who are you sending coins to?',
'Please enter their address:',
ui.ButtonSet.OK_CANCEL);
// Process the user's response.
var button = result.getSelectedButton();
var toAddress = result.getResponseText();
if (button == ui.Button.OK) {
// User clicked "OK".
// Validate Email Address
if (!verifyValidEmailAddress(toAddress)) {
ui.alert("Invalid Address");
return;
}
row = getRowOfAddress(toAddress);
if (row === undefined) {
var result = ui.alert(
'This address does not already have an account. A new account will be created.',
'Are you sure you want to continue?',
ui.ButtonSet.YES_NO);
// Process the user's response.
if (result == ui.Button.YES) {
// User clicked "Yes".
} else {
// User clicked "No" or X in the title bar.
ui.alert("TX Cancelled");
return;
}
}
var result = ui.prompt(
'How much would you like to send?',
'Please enter an amount:',
ui.ButtonSet.OK_CANCEL);
var button = result.getSelectedButton();
var amountInput = result.getResponseText();
if (button == ui.Button.OK) {
if (!isNaN(amountInput)){
amount = +amountInput
if (amount > senderBalance) {
ui.alert("Send amount is greater than your balance.");
return;
}
} else {
ui.alert("Must input a valid number.");
return;
}
} else {
// User clicked X in the title bar.
ui.alert("TX Cancelled");
return;
}
} else {
// User clicked X in the title bar.
ui.alert("TX Cancelled");
return;
}
txData = {
'action' : 'send',
'toAddress': toAddress,
'amount': amount
}
sendTxBox(JSON.stringify(txData));
/*
var response = UrlFetchApp.fetch('https://script.google.com/macros/s/AKfycby2aArgZZ9x7kZhdJdgTuLZ5uqeA4kXQ6ZtNI1DR5_P4t5vJqE/exec', options);
Logger.log(response.getContentText());
ui.alert(response.getContentText());
openUrl("https://sheetcoin.netlify.com/send?amount=" + amount + "&toAddress=" + toAddress);
*/
}
function depositCoinsPrompt() {
var ui = SpreadsheetApp.getUi(); // Same variations.
fromAddress = Session.getActiveUser().getEmail();
fromRow = getRowOfAddress(fromAddress);
var result = ui.prompt(
'How many coins are you depositing?',
'Please enter an amount:',
ui.ButtonSet.OK_CANCEL);
var button = result.getSelectedButton();
var amount
var amountInput = result.getResponseText();
if (button == ui.Button.OK) {
if (!isNaN(amountInput)){
amount = parseFloat(amountInput)
var result = ui.prompt(
'What Email address would you like to deposit to?',
'Please enter an EMAIL address:',
ui.ButtonSet.OK_CANCEL);
// Process the user's response.
var button = result.getSelectedButton();
var toAddress = result.getResponseText();
if (!verifyValidEmailAddress(toAddress)) {
ui.alert("Invalid Address");
return;
}
} else {
ui.alert("Must input a valid number.");
return;
}
} else {
// User clicked X in the title bar.
ui.alert("TX Cancelled");
return;
}
//
// txData = {
// 'action' : 'withdraw_request',
// 'toAddress': toAddress,
// 'amount': amount
// }
//
// sendTxBox(JSON.stringify(txData));
openUrl("https://sheetcoin.netlify.com/deposit?amount=" + amount + "&toAddress=" + toAddress);
}
function withdrawCoinsPrompt() {
var ui = SpreadsheetApp.getUi(); // Same variations.
fromAddress = Session.getActiveUser().getEmail();
fromRow = getRowOfAddress(fromAddress);
if (fromRow===undefined) {
ui.alert("You don't have any coins to withdraw yet.");
}
senderBalance = BALANCES_SHEET.getRange(fromRow, 2).getValue()
var result = ui.prompt(
'How many coins are you withdrawing?',
'Please enter an amount:',
ui.ButtonSet.OK_CANCEL);
var button = result.getSelectedButton();
var amountInput = result.getResponseText();
if (button == ui.Button.OK) {
if (!isNaN(amountInput)){
amount = parseFloat(amountInput)
if (amount > senderBalance) {
ui.alert("Withdraw amount is greater than your balance.");
return;
} else {
var result = ui.prompt(
'What Ethereum address would you like to withdraw to?',
'Please enter an ETHEREUM or ENS address:',
ui.ButtonSet.OK_CANCEL);
// Process the user's response.
var button = result.getSelectedButton();
var toAddress = result.getResponseText();
if (!verifyValidEthereumAddress(toAddress)) {
var options = {
headers: {
"x-rapidapi-host": "cindercloud.p.rapidapi.com",
"x-rapidapi-key": "R0Kjpa08ZABn1jQCwBaJ2BPp7GC79In2"
}
}
var response = UrlFetchApp.fetch('https://cindercloud.p.rapidapi.com/api/ethereum/ens/resolve/' + toAddress, options);
var jsonResponse = JSON.parse(response.getContentText())
if (jsonResponse.address === '0x0000000000000000000000000000000000000000') {
ui.alert('Invalid ETH Address or ENS Name');
return;
} else {
toAddress = jsonResponse.address
}
}
}
} else {
ui.alert("Must input a valid number.");
return;
}
} else {
// User clicked X in the title bar.
ui.alert("TX Cancelled");
return;
}
txData = {
'action' : 'withdraw_request',
'toAddress': toAddress,
'amount': amount
}
sendTxBox(JSON.stringify(txData));
// openUrl("https://sheetcoin.netlify.com/withdraw?amount=" + amount + "&toAddress=" + toAddress);
}
function getRowOfAddress(address){
var data = BALANCES_SHEET.getDataRange().getValues();
for(var i = 0; i<data.length;i++){
if(data[i][0] == address){
return i+1;
}
}
}
function getRowOfOwner(address){
var data = OWNERS_SHEET.getDataRange().getValues();
for(var i = 0; i<data.length;i++){
if(data[i][0] == address){
return i+1;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment