Skip to content

Commit

Permalink
First attempt to connect Ergo connector to Yoroi
Browse files Browse the repository at this point in the history
Includes some temporary testing such as "*" for target origin which
should be changed back later.

This communication is also just a first attempt and will open Yoroi in a
tab to initialize the index.js for communcation and this will be changed
in the future.
  • Loading branch information
rooooooooob committed Nov 19, 2020
1 parent 617ea63 commit 59cc3e4
Show file tree
Hide file tree
Showing 5 changed files with 214 additions and 89 deletions.
15 changes: 15 additions & 0 deletions chrome/extension/background.js
Expand Up @@ -10,3 +10,18 @@ const onYoroiIconClicked = () => {
};

chrome.browserAction.onClicked.addListener(debounce(onYoroiIconClicked, 500, { leading: true }));

function connectHandler(message, sender, sendResponse) {
if (sender.id == "egflibcdkfhnhdpbdlbgopagfdbkghbo") {
console.log("REAL(background.js)-yoroi received: " + JSON.stringify(message))
if (message.type == "yoroi_connect_request") {
chrome.tabs.create({ url: 'main_window.html', active: true });
sendResponse(true);
}
} else {
console.log("received message \"" + message + "\" from other sender: " + sender.id);
}
}

chrome.runtime.onMessageExternal.addListener(connectHandler);

77 changes: 77 additions & 0 deletions chrome/extension/index.js
Expand Up @@ -22,6 +22,20 @@ configure({ enforceActions: 'always' });
// Since Yoroi handles money, it's better to error our than proceed if an error occurs
BigNumber.DEBUG = true;

function getMethods(obj) {
var result = [];
for (var id in obj) {
try {
if (typeof(obj[id]) == "function") {
result.push(id + ": " + obj[id].toString());
}
} catch (err) {
result.push(id + ": inaccessible");
}
}
return result;
}

// Entry point into our application
const initializeYoroi: void => Promise<void> = async () => {
const api = await setupApi();
Expand All @@ -40,6 +54,7 @@ const initializeYoroi: void => Promise<void> = async () => {
createStores(api, actions, router);
})
};
//alert("yoroi.api = " + JSON.stringify(window.yoroi.stores.wallets));

const root = document.querySelector('#root');
if (root == null) {
Expand All @@ -49,8 +64,70 @@ const initializeYoroi: void => Promise<void> = async () => {
<App stores={stores} actions={actions} history={history} />,
root
);

chrome.runtime.sendMessage(
"egflibcdkfhnhdpbdlbgopagfdbkghbo",
{type: "yoroi_connected"},
);
};

addCloseListener();

window.addEventListener('load', initializeYoroi);

function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}

async function rpcHandler(message, sender, sendResponse) {
alert(`received ${JSON.stringify(message)} from rpcHandler`);
if (sender.id == "egflibcdkfhnhdpbdlbgopagfdbkghbo") {
console.log("REAL(index.js)-yoroi received: " + JSON.stringify(message))
if (message.type == "connector_rpc_request") {
switch (message.function) {
case "get_balance":
if (message.params[0] == "ERG") {
//sleep(5000).then(() => {
//window.yoroi.api.?.
const wallets = window.yoroi.stores.wallets;
console.log("wallets: " + wallets.publicDerivers);
const publicDeriver = wallets.first;
//alert();
//const getBalance = asGetBalance(publicDeriver);
publicDeriver.getBalance().then(x => {
sendResponse({
//ok: publicDeriver.getBalance()
ok: x
});
});
//});
} else {
sendResponse({
ok: 5
});
}
break;
case "sign_tx":
sendResponse({
err: {
code: 2,
info: "User rejected",
}
});
case "ping":
sendResponse({
ok: true,
});
default:
sendResponse({
err: "unknown RPC: " + message.function + "(" + message.params + ")"
})
break;
}
}
} else {
alert("received message \"" + message + "\" from other sender: " + sender.id);
}
}

chrome.runtime.onMessageExternal.addListener(rpcHandler);
16 changes: 15 additions & 1 deletion yoroi-ergo-connector/background.js
@@ -1,10 +1,24 @@
// TODO: put this somewhere common?
const yoroiExtensionId = "bgihpbbhciffmelcfbccneidnnmkcdhl";

chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {
//alert("received from page: " + JSON.stringify(sender));
//alert("background message: " + JSON.stringify(sender));
if (request.type == "init_page_action") {
chrome.pageAction.setPopup({
tabId: sender.tab.id,
popup: "main.html"
});
chrome.pageAction.show(sender.tab.id);
}
});

chrome.runtime.onMessageExternal.addListener(function(message, sender, sendResponse) {
//alert("background external message: " + JSON.stringify(message));
if (sender.id == yoroiExtensionId) {
if (message.type == "yoroi_connected") {
chrome.tabs.query({active: true}, function(tabs) {
chrome.tabs.sendMessage(tabs[0].id, {type: "yoroi_connected"});
});
}
}
});
187 changes: 103 additions & 84 deletions yoroi-ergo-connector/inject.js
@@ -1,28 +1,61 @@
// replace "*" with location.origin before committing on all postMessage calls

//const yoroiExtensionId = "eegbdfmlofnpgiiilnlboaamccblbobe";
const yoroiExtensionId = "bgihpbbhciffmelcfbccneidnnmkcdhl";

// sets up RPC communication with the connector + access check/request functions
const initialInject = `
var rpcUid = 0;
var rpcResolver = new Map();
var timeout = 0;
function _ergo_rpc_call(func, params) {
var connectRequests = [];
window.addEventListener("message", function(event) {
if (event.data.type == "connector_connected") {
if (event.data.err !== undefined) {
connectRequests.forEach(promise => promise.reject(event.data.err));
} else {
connectRequests.forEach(promise => promise.resolve(event.data.success));
}
}
});
function ergo_request_read_access() {
return new Promise(function(resolve, reject) {
window.postMessage({
type: "connector_rpc_request",
uid: rpcUid,
function: func,
params: params
}, location.origin);
rpcResolver.set(rpcUid, { resolve: resolve, reject: reject });
rpcUid += 1;
type: "connector_connect_request",
}, "*");
connectRequests.push({ resolve: resolve, reject: reject });
});
}
function ergo_request_read_access() {
return _ergo_rpc_call("ergo_request_read_access", []);
}
// TODO: fix or change back how RPCs work
// // disconnect detector
// setInterval(function() {
// if (timeout == 20) {
// window.dispatchEvent(new Event("ergo_wallet_disconnected"));
// }
// if (timeout == 25) {
// rpcResolver.forEach(function(rpc) {
// rpc.reject("timed out");
// });
// }
// timeout += 1;
// }, 1000);
// // ping sender
// setInterval(function() {
// _ergo_rpc_call("ping", []).then(function() {
// timeout = 0;
// });
// }, 10000);
`

// client-facing ergo object API
const apiInject = `
// RPC set-up
var rpcUid = 0;
var rpcResolver = new Map();
window.addEventListener("message", function(event) {
if (event.data.type == "connector_rpc_response") {
console.log("page received from connector: " + JSON.stringify(event.data) + " with source = " + event.source + " and origin = " + event.origin);
Expand All @@ -38,36 +71,26 @@ window.addEventListener("message", function(event) {
}
});
// disconnect detector
setInterval(function() {
if (timeout == 20) {
window.dispatchEvent(new Event("ergo_wallet_disconnected"));
}
if (timeout == 25) {
rpcResolver.forEach(function(rpc) {
rpc.reject("timed out");
});
}
timeout += 1;
}, 1000);
// ping sender
setInterval(function() {
_ergo_rpc_call("ping", []).then(function() {
timeout = 0;
});
}, 10000);
`

// client-facing ergo object API
const apiInject = `
class ErgoAPI {
get_balance(token_id = 'ERG') {
return _ergo_rpc_call("get_balance", [token_id]);
return this._ergo_rpc_call("get_balance", [token_id]);
}
sign_tx(tx) {
return _ergo_rpc_call("sign_tx", [tx]);
return this._ergo_rpc_call("sign_tx", [tx]);
}
_ergo_rpc_call(func, params) {
return new Promise(function(resolve, reject) {
window.postMessage({
type: "connector_rpc_request",
uid: rpcUid,
function: func,
params: params
}, "*");
rpcResolver.set(rpcUid, { resolve: resolve, reject: reject });
rpcUid += 1;
});
}
}
Expand All @@ -92,63 +115,59 @@ function injectIntoPage(code) {

injectIntoPage(initialInject);

chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {
//alert("content script message: " + JSON.stringify(request));
if (request.type == "yoroi_connected") {
// inject full API here
if (injectIntoPage(apiInject)) {
chrome.runtime.sendMessage({type: "init_page_action"});
} else {
alert("failed to inject Ergo API");
// TODO: return an error instead here if injection fails?
}
window.postMessage({
type: "connector_connected",
success: true
}, "*");
}
});

window.addEventListener("message", function(event) {
function sendRpcToYoroi(injectApiOnSuccess) {
if (event.data.type == "connector_rpc_request") {
console.log("connector received from page: " + JSON.stringify(event.data) + " with source = " + event.source + " and origin = " + event.origin);
chrome.runtime.sendMessage(
"eegbdfmlofnpgiiilnlboaamccblbobe",
yoroiExtensionId,
event.data,
{},
function(response) {
if (injectApiOnSuccess) {
// inject full API here
if (response.ok === true) {
if (injectIntoPage(apiInject)) {
chrome.runtime.sendMessage(
{type:"init_page_action"});
} else {
alert("failed to inject Ergo API");
// TODO: return an error instead here if injection fails?
}
} else {
// ???
}
}
window.postMessage({
type: "connector_rpc_response",
uid: event.data.uid,
return: response
}, location.origin);
}, "*");
});
}

if (event.data.type == "connector_rpc_request") {
console.log("connector received from page: " + JSON.stringify(event.data) + " with source = " + event.source + " and origin = " + event.origin);
if (event.data.function == "ergo_request_read_access") {
chrome.storage.local.get("whitelist", function(result) {
alert(JSON.stringify(result));
let whitelist = Object.keys(result).length === 0 ? [] : result.whitelist;
if (!whitelist.includes(location.hostname)) {
if (confirm(`Allow access of ${location.hostname} to Ergo-Yoroi connector?`)) {
if (confirm(`Save ${location.hostname} to whitelist?`)) {
whitelist.push(location.hostname);
chrome.storage.local.set({whitelist:whitelist});
}
sendRpcToYoroi(true);
} else {
// user refused - skip communication with Yoroi
window.postMessage({
type: "connector_rpc_response",
uid: event.data.uid,
return: {ok: false}
}, location.origin);
} else if (event.data.type == "connector_connect_request") {
// TODO: add timeout somehow?
chrome.storage.local.get("whitelist", function(result) {
let whitelist = Object.keys(result).length === 0 ? [] : result.whitelist;
if (!whitelist.includes(location.hostname)) {
if (confirm(`Allow access of ${location.hostname} to Ergo-Yoroi connector?`)) {
if (confirm(`Save ${location.hostname} to whitelist?`)) {
whitelist.push(location.hostname);
chrome.storage.local.set({whitelist: whitelist});
}
chrome.runtime.sendMessage(yoroiExtensionId, {type: "yoroi_connect_request"});
} else {
// already in whitelist
sendRpcToYoroi(true);
// user refused - skip communication with Yoroi
window.postMessage({
type: "connector_connected",
success: false
}, "*");
}
});
} else {
sendRpcToYoroi(false);
}
} else {
// already in whitelist
chrome.runtime.sendMessage(yoroiExtensionId, {type: "yoroi_connect_request"});
}
});
}
});

0 comments on commit 59cc3e4

Please sign in to comment.