Created
May 7, 2020 09:51
-
-
Save okwme/01583a547b1a2550a9d3eee94cd4699c to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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