Skip to content

Commit

Permalink
Merge pull request #159 from palladians/chore/rise-up-fallen-web-conn…
Browse files Browse the repository at this point in the history
…ector

Chore/rise up fallen web connector
  • Loading branch information
mrcnk authored Apr 8, 2024
2 parents 3ac6c35 + 679188d commit c508773
Show file tree
Hide file tree
Showing 109 changed files with 2,349 additions and 3,649 deletions.
11 changes: 8 additions & 3 deletions .eslintrc.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
{
"extends": ["plugin:prettier/recommended", "eslint:recommended", "plugin:@typescript-eslint/recommended"],
"extends": [
"plugin:prettier/recommended",
"eslint:recommended",
"plugin:@typescript-eslint/recommended"
],
"parser": "@typescript-eslint/parser",
"plugins": ["@typescript-eslint", "simple-import-sort", "import"],
"root": true,
Expand All @@ -8,7 +12,8 @@
"simple-import-sort/exports": "error",
"@typescript-eslint/no-unused-vars": "error",
"@typescript-eslint/no-explicit-any": "warn",
"import/no-extraneous-dependencies": "error"
"@typescript-eslint/no-inferrable-types": "error",
"import/no-extraneous-dependencies": "error",
"require-await": "error"
}
}

4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

⚠️ Work in progress ⚠️
🚨 Now in Beta 🚨

</div>

Welcome to Pallad an experimental and progressive Mina Protocol wallet!
Expand All @@ -23,15 +24,14 @@ In the vast and evolving world of Web3, a wallet is not just a tool; it's the ga

Pallad is at the frontier, leading the way in innovation and user-centric design. We're not just building a wallet; we're crafting the cornerstone of zero-knowledge application interaction design. With Pallad, you're stepping into the future.


## Getting Started 🚀

### Prerequisite 📌

- [NVM](https://github.com/nvm-sh/nvm)
- pnpm

### Installation 💻
### Installation 💻

Make sure you're on the right Node.js version, and you got pnpm installed.

Expand Down
26 changes: 13 additions & 13 deletions apps/extension/e2e/pom/onboarding.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,45 +24,45 @@ export class OnboardingPom extends BasePom {
goto() {
return this.page.goto(`chrome-extension://${this.extensionId}/index.html`)
}
async startRestoring() {
startRestoring() {
const restoreWalletButton = this.page.getByTestId(
TestId.RESTORE_WALLET_BUTTON
)
return restoreWalletButton.click()
}
async startCreating() {
startCreating() {
const createWalletButton = this.page.getByTestId(
TestId.CREATE_WALLET_BUTTON
)
return createWalletButton.click()
}
async fillWalletName(walletName: string) {
fillWalletName(walletName: string) {
const walletNameInput = this.page.getByTestId(TestId.WALLET_NAME_INPUT)
return walletNameInput.fill(walletName)
}
async fillSpendingPassword(spendingPassword: string) {
fillSpendingPassword(spendingPassword: string) {
const spendingPasswordInput = this.page.getByTestId(
TestId.SPENDING_PASSWORD_INPUT
)
return spendingPasswordInput.fill(spendingPassword)
}
async toggleTos() {
toggleTos() {
const tosCheckbox = this.page.getByTestId(TestId.TOS_CHECKBOX)
return tosCheckbox.click()
}
async goNext() {
goNext() {
const nextButton = this.page.getByTestId(TestId.NEXT_BUTTON)
return nextButton.click()
}
async assertNextDisabled() {
assertNextDisabled() {
const nextButton = this.page.getByTestId(TestId.NEXT_BUTTON)
expect(nextButton.isDisabled).toBeTruthy()
}
async goBack() {
goBack() {
const backButton = this.page.getByTestId(TestId.BACK_BUTTON)
return backButton.click()
}
async confirmAlone() {
confirmAlone() {
const confirmAloneButton = this.page.getByTestId(TestId.CONFIRM_ALONE)
return confirmAloneButton.click()
}
Expand All @@ -72,7 +72,7 @@ export class OnboardingPom extends BasePom {
await field.fill(mnemonic[i])
}
}
async getAddressTruncated() {
getAddressTruncated() {
const addressTruncated = this.page.getByTestId(TestId.ADDRESS_TRUNCATED)
return addressTruncated.textContent()
}
Expand All @@ -82,7 +82,7 @@ export class OnboardingPom extends BasePom {
.all()
return await Promise.all(mnemonicWords.map((word) => word.innerText()))
}
async toggleMnemonicWritten() {
toggleMnemonicWritten() {
const mnemonicWritten = this.page.getByTestId(
TestId.MNEMONIC_WRITTEN_CHECKBOX
)
Expand All @@ -95,13 +95,13 @@ export class OnboardingPom extends BasePom {
const [, confirmationIndex] = inputLabel.split('#')
return parseInt(confirmationIndex) - 1
}
async fillMnemonicConfirmation(specificWord: string) {
fillMnemonicConfirmation(specificWord: string) {
const mnemonicConfirmationInput = this.page.getByTestId(
TestId.MNEMONIC_CONFIRMATION_INPUT
)
return mnemonicConfirmationInput.fill(specificWord)
}
async getMinaBalance() {
getMinaBalance() {
const minaBalance = this.page.getByTestId(TestId.MINA_BALANCE)
return minaBalance.innerText()
}
Expand Down
93 changes: 93 additions & 0 deletions apps/extension/e2e/provider-enable.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import { expect, test } from './extension'
import { OnboardingPom } from './pom/onboarding'

const VALIDATOR = 'B62qjsV6WQwTeEWrNrRRBP6VaaLvQhwWTnFi4WP4LQjGvpfZEumXzxb'

// Note: These tests are manual
test('enable window.mina and handle pop-up on a specific webpage', async ({
page,
extensionId
}) => {
// Restore the wallet or perform any setup required by your extension before interacting with the webpage.
const onboardingPom = new OnboardingPom({ page, extensionId })
await onboardingPom.restoreTestWallet()

// Navigate to the webpage where the injected script should be interacted with.
await page.goto('https://google.com')

// Verify that window.mina is available
const minaExists = await page.evaluate(() => window.mina !== undefined)
expect(minaExists).toBe(true)

// Trigger window.mina.enable() which should open the pop-up
const enableResponse = await page.evaluate(() => window.mina.enable())
/**
Click "Yes" manually
*/
expect(enableResponse.result.length).toBe(1)
expect(enableResponse.result[0]).toBe(VALIDATOR)

const account = await page.evaluate(() =>
window.mina.request({ method: 'mina_accounts' })
)
expect(account.result.length).toBe(1)
expect(account.result[0]).toBe(VALIDATOR)

const responseChainId = await page.evaluate(() =>
window.mina.request({ method: 'mina_chainId' })
)
expect(responseChainId.result).not.toBe('...')

const responseBalance = await page.evaluate(() =>
window.mina.request({ method: 'mina_getBalance' })
)
expect(responseBalance.result).not.toBe(undefined)

await page.evaluate(() =>
window.mina.request({
method: 'mina_setState',
params: {
objectName: 'New Example Credential',
object: {
'@context': ['https://www.w3.org/2018/credentials/v1'],
id: 'http://example.edu/credentials/3732',
type: ['VerifiableCredential', 'UniversityDegreeCredential'],
issuer: 'University of Example',
issuanceDate: '2010-01-01T00:00:00Z',
credentialSubject: {
id: 'did:mina:B62qjsV6WQwTeEWrNrRRBP6VaaLvQhwWTnFi4WP4LQjGvpfZEumXzxb',
degree: {
type: 'BachelorDegree',
name: 'Bachelor of Science and Arts'
}
},
proof: {
type: 'Kimchi',
created: '2023-09-19T12:40:16Z',
proof: {
publicInput: ['0'],
publicOutput: ['1'],
maxProofsVerified: 0,
proof: 'KChzdGF0ZW1...SkpKSkp'
}
}
}
}
})
)
/**
Enter password manually
*/

const responseGetState = await page.evaluate(() =>
window.mina.request({
method: 'mina_getState',
params: { query: { issuer: 'University of Example' }, props: [] }
})
)
/**
Enter password manually
*/

expect(responseGetState.result.length).toBe(2)
})
4 changes: 2 additions & 2 deletions apps/extension/manifest.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ const [major, minor, patch, label = '0'] = version
// split into version parts
.split(/[.-]/)

export default defineManifest(async (env) => ({
export default defineManifest((env) => ({
manifest_version: 3,
name: env.mode === 'DEVELOPMENT' ? '[DEV] Pallad' : 'Pallad',
description:
Expand Down Expand Up @@ -40,7 +40,7 @@ export default defineManifest(async (env) => ({
],
web_accessible_resources: [
{
resources: ['pallad_rpc.js', 'prompt.html', 'prompt.js'],
resources: ['pallad_rpc.js'],
matches: ['https://*/*']
}
],
Expand Down
28 changes: 28 additions & 0 deletions apps/extension/prompt.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Pallad</title>
<link rel="stylesheet" href="/src/assets/fonts.css" />
<style>
:root {
--popup: 1;
}

#root {
min-height: 100vh;
display: flex;
}
</style>
<script type="module">
import { Buffer } from 'buffer'
window.Buffer = Buffer
</script>
</head>

<body>
<div id="root"></div>
<script type="module" src="/src/prompt.tsx"></script>
</body>
</html>
70 changes: 63 additions & 7 deletions apps/extension/public/pallad_rpc.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,21 @@
/* eslint-disable */
function debounce(func, wait, immediate) {
let timeout
return function (...args) {
return new Promise((resolve) => {
clearTimeout(timeout)
timeout = setTimeout(() => {
timeout = null
if (!immediate) {
Promise.resolve(func.apply(this, [...args])).then(resolve)
}
}, wait)
if (immediate && !timeout) {
Promise.resolve(func.apply(this, [...args])).then(resolve)
}
})
}
}
const BROADCAST_CHANNEL_ID = 'pallad'
const callPalladAsync = ({ method, payload }) =>
new Promise((resolve, reject) => {
Expand All @@ -18,26 +35,65 @@ const callPalladAsync = ({ method, payload }) =>
})
return channel.close()
})
const debouncedCall = debounce(callPalladAsync, 300)
const init = () => {
window.mina = {
wallet: {
name: 'Pallad',
icon: 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTY1IiBoZWlnaHQ9IjIwMCIgdmlld0JveD0iMCAwIDE2NSAyMDAiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxwYXRoIGQ9Ik04Mi4zNTMgMTg4LjIzNkM4Mi4zNTMgMTg4LjIzNiAxNTIuOTQxIDE1Mi45NDIgMTUyLjk0MSAxMDAuMDAxVjM4LjIzNjJMODIuMzUzIDExLjc2NTZMMTEuNzY0OCAzOC4yMzYyVjEwMC4wMDFDMTEuNzY0OCAxNTIuOTQyIDgyLjM1MyAxODguMjM2IDgyLjM1MyAxODguMjM2WiIgc3Ryb2tlPSJ1cmwoI3BhaW50MF9saW5lYXJfMTUxXzQ5NikiIHN0cm9rZS13aWR0aD0iMTcuNjc4NiIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIi8+CjxwYXRoIGQ9Ik02OS4zNTA2IDQ4LjYzNzdDNzEuMTE5NSA0NC4xMzYzIDc1LjQ2MzYgNDEuMTc2MyA4MC4zMDAxIDQxLjE3NjNIODIuMzEwMUM4Ny4xNDE4IDQxLjE3NjMgOTEuNDgxOCA0NC4xMyA5My4yNTQyIDQ4LjYyNDVMMTIxLjA5NSAxMTkuMjEzQzEyNC4xMzkgMTI2LjkzMyAxMTguNDQ5IDEzNS4yOTQgMTEwLjE1MSAxMzUuMjk0SDEwOC4wODdMNzguNjAxMiA1NC45OTc3SDgzLjQ3MThMNTMuOTg2MSAxMzUuMjk0SDUyLjU1ODVDNDQuMjY1NyAxMzUuMjk0IDM4LjU3NTcgMTI2Ljk0NCA0MS42MDkgMTE5LjIyNkw2OS4zNTA2IDQ4LjYzNzdaIiBmaWxsPSJ1cmwoI3BhaW50MV9saW5lYXJfMTUxXzQ5NikiLz4KPGRlZnM+CjxsaW5lYXJHcmFkaWVudCBpZD0icGFpbnQwX2xpbmVhcl8xNTFfNDk2IiB4MT0iMTYxLjc2NSIgeTE9IjUuODgzMjciIHgyPSI4LjgyMzYiIHkyPSIyMDAuMDAxIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+CjxzdG9wIHN0b3AtY29sb3I9IiMxRkQ3RkYiLz4KPHN0b3Agb2Zmc2V0PSIxIiBzdG9wLWNvbG9yPSIjOEQ3QUZGIi8+CjwvbGluZWFyR3JhZGllbnQ+CjxsaW5lYXJHcmFkaWVudCBpZD0icGFpbnQxX2xpbmVhcl8xNTFfNDk2IiB4MT0iMTYxLjc2NSIgeTE9IjIuOTQwOTgiIHgyPSI1Ljg4MjM5IiB5Mj0iMTk3LjA1OSIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiPgo8c3RvcCBzdG9wLWNvbG9yPSIjMUZEN0ZGIi8+CjxzdG9wIG9mZnNldD0iMSIgc3RvcC1jb2xvcj0iIzhEN0FGRiIvPgo8L2xpbmVhckdyYWRpZW50Pgo8L2RlZnM+Cjwvc3ZnPgo='
},
request: async ({ method, params }) =>
await callPalladAsync({ method, payload: params }),
debouncedCall({ method, payload: params }),
enable: async () => {
const response = await callPalladAsync({
return debouncedCall({
method: 'enable',
payload: {}
payload: { origin: window.location.origin }
})
return response
},
isPallad: true,
isConnected: async () => {
const response = await callPalladAsync({
return debouncedCall({
method: 'isConnected',
payload: {}
payload: { origin: window.location.origin }
})
},
otherOn: async () => {
return 'hello world!'
},
/*
Note: `listenerId` is used as a placeholder to identify listener functions.
Since functions can't be serialized over postMessage, you need to implement
a system in your background script to manage listeners and associate them with
IDs. When an event occurs, you can then send a message back to the content
script to invoke the appropriate listener by ID.
*/
// I don't think we need listenerId for `on`, `once`
on: async (event, listenerId) => {
return debouncedCall({
method: 'on',
payload: { event, listenerId }
})
},
/*once: async (event, listenerId) => {
const response = await debouncedCall({
method: 'once',
payload: { event, listenerId }
})
return response
},

removeListener: async (event, listenerId) => {
const response = await debouncedCall({
method: 'removeListener',
payload: { event, listenerId }
})
return response
},*/
off: async (event, listenerId) => {
return debouncedCall({
method: 'off',
payload: { event, listenerId }
})
}
}
}
init()
Loading

0 comments on commit c508773

Please sign in to comment.