-
Notifications
You must be signed in to change notification settings - Fork 38
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Ziga/gateway metamask connect button #1579
Merged
Merged
Changes from all commits
Commits
Show all changes
9 commits
Select commit
Hold shift + click to select a range
1a226b1
wip
zkokelj bf0177e
adding connect button
zkokelj 7a2a94e
add different names for different testnets
zkokelj f36cb8b
fix Obscuro Gateway UX flow
zkokelj 99c4ddf
handle accountsChanged
zkokelj 532915c
fix multiple signatures bug
zkokelj af2e706
favicon fix
zkokelj 40d43bc
display if joining OG is not successful
zkokelj 7f5de17
Merge branch 'main' into ziga/gateway_metamask_connect_button
zkokelj File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
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
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
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,7 +5,9 @@ const idAddAccount = "addAccount"; | |
const idAddAllAccounts = "addAllAccounts"; | ||
const idRevokeUserID = "revokeUserID"; | ||
const idStatus = "status"; | ||
const obscuroGatewayVersion = "v1" | ||
const idAccountsTable = "accountsTable"; | ||
const idTableBody = "tableBody"; | ||
const obscuroGatewayVersion = "v1"; | ||
const pathJoin = obscuroGatewayVersion + "/join/"; | ||
const pathAuthenticate = obscuroGatewayVersion + "/authenticate/"; | ||
const pathQuery = obscuroGatewayVersion + "/query/"; | ||
|
@@ -20,6 +22,7 @@ const jsonHeaders = { | |
}; | ||
|
||
const metamaskPersonalSign = "personal_sign"; | ||
const obscuroChainIDHex = "0x" + obscuroChainIDDecimal.toString(16); // Convert to hexadecimal and prefix with '0x' | ||
|
||
function isValidUserIDFormat(value) { | ||
return typeof value === 'string' && value.length === 64; | ||
|
@@ -50,6 +53,19 @@ async function fetchAndDisplayVersion() { | |
} | ||
} | ||
|
||
|
||
function getNetworkName(gatewayAddress) { | ||
switch(gatewayAddress) { | ||
case 'https://uat-testnet.obscu.ro/': | ||
return 'Obscuro UAT-Testnet'; | ||
case 'https://dev-testnet.obscu.ro/': | ||
return 'Obscuro Dev-Testnet'; | ||
default: | ||
return 'Obscuro Testnet'; | ||
} | ||
} | ||
|
||
|
||
function getRPCFromUrl(gatewayAddress) { | ||
// get the correct RPC endpoint for each network | ||
switch(gatewayAddress) { | ||
|
@@ -68,14 +84,13 @@ function getRPCFromUrl(gatewayAddress) { | |
|
||
async function addNetworkToMetaMask(ethereum, userID, chainIDDecimal) { | ||
// add network to MetaMask | ||
let chainIdHex = "0x" + chainIDDecimal.toString(16); // Convert to hexadecimal and prefix with '0x' | ||
try { | ||
await ethereum.request({ | ||
method: 'wallet_addEthereumChain', | ||
params: [ | ||
{ | ||
chainId: chainIdHex, | ||
chainName: 'Obscuro Testnet', | ||
chainId: obscuroChainIDHex, | ||
chainName: getNetworkName(obscuroGatewayAddress), | ||
nativeCurrency: { | ||
name: 'Sepolia Ether', | ||
symbol: 'ETH', | ||
|
@@ -95,7 +110,6 @@ async function addNetworkToMetaMask(ethereum, userID, chainIDDecimal) { | |
|
||
async function authenticateAccountWithObscuroGateway(ethereum, account, userID) { | ||
const isAuthenticated = await accountIsAuthenticated(account, userID) | ||
|
||
if (isAuthenticated) { | ||
return "Account is already authenticated" | ||
} | ||
|
@@ -152,17 +166,20 @@ function getRandomIntAsString(min, max) { | |
return randomInt.toString(); | ||
} | ||
|
||
|
||
async function getUserID() { | ||
try { | ||
return await provider.send('eth_getStorageAt', ["getUserID", getRandomIntAsString(0, 1000), null]) | ||
if (await isObscuroChain()) { | ||
return await provider.send('eth_getStorageAt', ["getUserID", getRandomIntAsString(0, 1000), null]) | ||
} else { | ||
return null | ||
} | ||
}catch (e) { | ||
console.log(e) | ||
return null; | ||
} | ||
} | ||
|
||
async function connectAccount() { | ||
async function connectAccounts() { | ||
try { | ||
return await window.ethereum.request({ method: 'eth_requestAccounts' }); | ||
} catch (error) { | ||
|
@@ -172,6 +189,18 @@ async function connectAccount() { | |
} | ||
} | ||
|
||
async function isMetamaskConnected() { | ||
let accounts; | ||
try { | ||
accounts = await provider.listAccounts() | ||
return accounts.length > 0; | ||
|
||
} catch (error) { | ||
console.log("Unable to get accounts") | ||
} | ||
return false | ||
} | ||
|
||
// Check if Metamask is available on mobile or as a plugin in browser | ||
// (https://docs.metamask.io/wallet/how-to/integrate-with-mobile/) | ||
function checkIfMetamaskIsLoaded() { | ||
|
@@ -225,113 +254,125 @@ async function populateAccountsTable(document, tableBody, userID) { | |
} | ||
} | ||
|
||
async function isObscuroChain() { | ||
let currentChain = await ethereum.request({ method: 'eth_chainId' }); | ||
return currentChain === obscuroChainIDHex | ||
} | ||
|
||
async function switchToObscuroNetwork() { | ||
try { | ||
await ethereum.request({ | ||
method: 'wallet_switchEthereumChain', | ||
params: [{ chainId: obscuroChainIDHex }], | ||
}); | ||
return 0 | ||
} catch (switchError) { | ||
return switchError.code | ||
} | ||
return -1 | ||
} | ||
|
||
const initialize = async () => { | ||
const joinButton = document.getElementById(idJoin); | ||
const addAccountButton = document.getElementById(idAddAccount); | ||
const addAllAccountsButton = document.getElementById(idAddAllAccounts); | ||
const revokeUserIDButton = document.getElementById(idRevokeUserID); | ||
const statusArea = document.getElementById(idStatus); | ||
|
||
const accountsTable = document.getElementById('accountsTable') | ||
const tableBody = document.getElementById('tableBody'); | ||
const accountsTable = document.getElementById(idAccountsTable) | ||
const tableBody = document.getElementById(idTableBody); | ||
// getUserID from the gateway with getStorageAt method | ||
let userID = await getUserID() | ||
|
||
|
||
// load the current version | ||
await fetchAndDisplayVersion(); | ||
|
||
// check if userID exists and has a correct type and length (is valid) and display either | ||
// option to join or to add a new account to existing user | ||
if (isValidUserIDFormat(userID)) { | ||
joinButton.style.display = "none" | ||
addAccountButton.style.display = "block" | ||
addAllAccountsButton.style.display = "block" | ||
revokeUserIDButton.style.display = "block" | ||
accountsTable.style.display = "block" | ||
await populateAccountsTable(document, tableBody, userID) | ||
} else { | ||
function displayOnlyJoin() { | ||
joinButton.style.display = "block" | ||
addAccountButton.style.display = "none" | ||
revokeUserIDButton.style.display = "none" | ||
accountsTable.style.display = "none" | ||
} | ||
|
||
joinButton.addEventListener(eventClick, async () => { | ||
// join Obscuro Gateway | ||
const joinResp = await fetch( | ||
pathJoin, { | ||
method: methodGet, | ||
headers: jsonHeaders, | ||
} | ||
); | ||
if (!joinResp.ok) { | ||
statusArea.innerText = "Failed to join. \nError: " + joinResp | ||
return | ||
} | ||
|
||
// save userID to the localStorage and hide button that enables users to join | ||
userID = await joinResp.text(); | ||
async function displayConnectedAndJoinedSuccessfully() { | ||
joinButton.style.display = "none" | ||
|
||
// add Obscuro network to Metamask | ||
let networkAdded = await addNetworkToMetaMask(ethereum, userID, obscuroChainIDDecimal) | ||
if (!networkAdded) { | ||
statusArea.innerText = "Failed to add network" | ||
return | ||
} | ||
statusArea.innerText = "Successfully joined Obscuro Gateway"; | ||
// show users an option to add another account and revoke userID | ||
addAccountButton.style.display = "block" | ||
addAllAccountsButton.style.display = "block" | ||
revokeUserIDButton.style.display = "block" | ||
accountsTable.style.display = "block" | ||
await populateAccountsTable(document, tableBody, userID) | ||
}) | ||
} | ||
|
||
addAccountButton.addEventListener(eventClick, async () => { | ||
// check if we have userID and it is the correct length | ||
if (!isValidUserIDFormat(userID)) { | ||
statusArea.innerText = "\n Please join Obscuro network first" | ||
joinButton.style.display = "block" | ||
addAccountButton.style.display = "none" | ||
async function displayCorrectScreenBasedOnMetamaskAndUserID() { | ||
// check if we are on Obscuro Chain | ||
if(await isObscuroChain()){ | ||
// check if we have valid userID in rpcURL | ||
if (isValidUserIDFormat(userID)) { | ||
return await displayConnectedAndJoinedSuccessfully() | ||
} | ||
} | ||
return displayOnlyJoin() | ||
} | ||
|
||
await connectAccount() | ||
// load the current version | ||
await fetchAndDisplayVersion(); | ||
|
||
// Get an account and prompt user to sign joining with a selected account | ||
const account = await provider.getSigner().getAddress(); | ||
if (account.length === 0) { | ||
statusArea.innerText = "No MetaMask accounts found." | ||
return | ||
} | ||
let authenticateAccountStatus = await authenticateAccountWithObscuroGateway(ethereum, account, userID) | ||
//statusArea.innerText = "\n Authentication status: " + authenticateAccountStatus | ||
accountsTable.style.display = "block" | ||
await populateAccountsTable(document, tableBody, userID) | ||
}) | ||
await displayCorrectScreenBasedOnMetamaskAndUserID() | ||
|
||
addAllAccountsButton.addEventListener(eventClick, async () => { | ||
// check if we have userID and it is the correct length | ||
if (!isValidUserIDFormat(userID)) { | ||
statusArea.innerText = "\n Please join Obscuro network first" | ||
joinButton.style.display = "block" | ||
addAccountButton.style.display = "none" | ||
} | ||
joinButton.addEventListener(eventClick, async () => { | ||
// check if we are on an obscuro chain | ||
if (await isObscuroChain()) { | ||
userID = await getUserID() | ||
if (!isValidUserIDFormat(userID)) { | ||
statusArea.innerText = "Please remove existing Obscuro network from metamask and start again." | ||
} | ||
} else { | ||
// we are not on an Obscuro network - try to switch | ||
let switched = await switchToObscuroNetwork(); | ||
// error 4902 means that the chain does not exist | ||
if (switched === 4902 || !isValidUserIDFormat(await getUserID())) { | ||
// join the network | ||
const joinResp = await fetch( | ||
pathJoin, { | ||
method: methodGet, | ||
headers: jsonHeaders, | ||
}); | ||
if (!joinResp.ok) { | ||
console.log("Error joining Obscuro Gateway") | ||
statusArea.innerText = "Error joining Obscuro Gateway. Please try again later." | ||
return | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should something be logged to the console and/or user here ? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Added console log and message to user to try again later. |
||
} | ||
userID = await joinResp.text(); | ||
|
||
// add Obscuro network | ||
await addNetworkToMetaMask(window.ethereum, userID) | ||
} | ||
|
||
await connectAccount() | ||
// we have to check if user has accounts connected with metamask - and promt to connect if not | ||
if (!await isMetamaskConnected()) { | ||
await connectAccounts(); | ||
} | ||
|
||
// Get an account and prompt user to sign joining with selected account | ||
const accounts = await provider.listAccounts(); | ||
if (accounts.length === 0) { | ||
statusArea.innerText = "No MetaMask accounts found." | ||
return | ||
} | ||
// connect all accounts | ||
// Get an accounts and prompt user to sign joining with a selected account | ||
const accounts = await provider.listAccounts(); | ||
if (accounts.length === 0) { | ||
statusArea.innerText = "No MetaMask accounts found." | ||
return | ||
} | ||
|
||
userID = await getUserID(); | ||
for (const account of accounts) { | ||
await authenticateAccountWithObscuroGateway(ethereum, account, userID) | ||
accountsTable.style.display = "block" | ||
await populateAccountsTable(document, tableBody, userID) | ||
} | ||
|
||
for (const account of accounts) { | ||
let authenticateAccountStatus = await authenticateAccountWithObscuroGateway(ethereum, account, userID) | ||
accountsTable.style.display = "block" | ||
await populateAccountsTable(document, tableBody, userID) | ||
// if accounts change we want to give user chance to add them to Obscuro | ||
window.ethereum.on('accountsChanged', async function (accounts) { | ||
if (isValidUserIDFormat(await getUserID())) { | ||
userID = await getUserID(); | ||
for (const account of accounts) { | ||
await authenticateAccountWithObscuroGateway(ethereum, account, userID) | ||
accountsTable.style.display = "block" | ||
await populateAccountsTable(document, tableBody, userID) | ||
} | ||
} | ||
}); | ||
|
||
await displayConnectedAndJoinedSuccessfully() | ||
} | ||
}) | ||
|
||
|
@@ -341,17 +382,11 @@ const initialize = async () => { | |
await populateAccountsTable(document, tableBody, userID) | ||
|
||
if (result) { | ||
joinButton.style.display = "block"; | ||
revokeUserIDButton.style.display = "none"; | ||
addAllAccountsButton.style.display = "none"; | ||
statusArea.innerText = "Revoking UserID successful. Please remove current network from Metamask." | ||
addAccountButton.style.display = "none"; | ||
accountsTable.style.display = "none" | ||
displayOnlyJoin() | ||
} else { | ||
statusArea.innerText = "Revoking UserID failed"; | ||
} | ||
}) | ||
|
||
} | ||
|
||
window.addEventListener(eventDomLoaded, checkIfMetamaskIsLoaded); | ||
|
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should this return false ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is already returning false (after catch statement is executed)