Skip to content

Commit

Permalink
Link to specific accounts from the address bar (#428)
Browse files Browse the repository at this point in the history
  • Loading branch information
Tbaut authored Nov 9, 2023
1 parent 907445c commit 39e7763
Show file tree
Hide file tree
Showing 29 changed files with 685 additions and 135 deletions.
5 changes: 4 additions & 1 deletion packages/ui/cypress/fixtures/knownMultisigs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ export const knownMultisigs = {
'test-multisig-1': {
address: '5CmwqwwLEkEtsmB9gFaTJdCfurz33xyggHnvwHaGKtvmQNxq',
threshold: 2,
signatories: [testAccounts['Test Account 1'].address, testAccounts['Test Account 2'].address]
signatories: [
testAccounts['Multisig Member Account 1'].address,
testAccounts['Multisig Member Account 2'].address
]
}
}
6 changes: 4 additions & 2 deletions packages/ui/cypress/fixtures/landingData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,7 @@ export const baseUrl = 'http://localhost:3333'
export const networkParams = 'network=rococo'
export const landingPageUrl = `${baseUrl}?${networkParams}`
export const settingsPageUrl = `${baseUrl}/settings?${networkParams}`
const WATCH_ACCOUNT_ANCHOR = '#watched-accounts'
export const settingsPageWatchAccountUrl = `${settingsPageUrl}${WATCH_ACCOUNT_ANCHOR}`
const WATCH_ACCOUNT_ANCHOR = 'watched-accounts'
export const settingsPageWatchAccountUrl = `${settingsPageUrl}#${WATCH_ACCOUNT_ANCHOR}`
export const landingPageAddressUrl = (address: string) =>
`${baseUrl}?${networkParams}&address=${address}`
15 changes: 11 additions & 4 deletions packages/ui/cypress/fixtures/testAccounts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,17 @@ export interface InjectedAccountWitMnemonic extends InjectedAccount {
}

export const testAccounts = {
'Test Account 1': {
'Multisig Member Account 1': {
address: '5H679cx9tkuHqyReUgBxeTqXKjVikVwLySDH1buiYuoqhi2w',
publicKey: '0xde3ed24acdfe71c13b4d42539c5390ddee147ba6b29b0593ce842e77ff034445',
name: 'Test Account 1',
name: 'Multisig Member Account 1',
type: 'sr25519',
mnemonic: 'climb worth pioneer mushroom cloth expose tube high half final curtain toward'
} as InjectedAccountWitMnemonic,
'Test Account 2': {
'Multisig Member Account 2': {
address: '5GCXBrumiRDQ8KQsgbG39HdBNLQKt6XCQbeHZJccGdZbYTgt',
publicKey: '0xb6e6fb4f2a2268bf6e8a211d958cbf602881418bcc533216cadfae3e24785f28',
name: 'Test Account 2',
name: 'Multisig Member Account 2',
type: 'sr25519',
mnemonic: 'divorce lottery slender again adapt process slow pigeon suit chase news begin'
} as InjectedAccountWitMnemonic,
Expand All @@ -33,5 +33,12 @@ export const testAccounts = {
name: 'Non Multisig Member 2',
type: 'sr25519',
mnemonic: 'erosion never fee pill vocal fetch enforce soap betray zero answer hollow'
} as InjectedAccountWitMnemonic,
'Many Multisig And Pure Member 1': {
address: '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY',
publicKey: '0xd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d',
name: 'Many Multisig And Pure Member 1',
type: 'sr25519',
mnemonic: 'bottom drive obey lake curtain smoke basket hold race lonely fit walk//Alice'
} as InjectedAccountWitMnemonic
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ export const watchSignatories = [
address: '5GGjPYsz8B8mxAzNScFNDPkZ1g97VWFCPCMexPSkPnibPBez',
name: 'Pure Signatory 1',
type: 'sr25519',
mnemonic: 'citizen heavy warrior cattle enter chef label split differ seek turtle gorilla'
mnemonic: 'citizen heavy warrior cattle enter chef label split differ seek turtle gorilla',
publickey: '0xba1d098e50bdca49f03b9f0c702a65762a04dfc5868ae1c3788a2bc1939dbf4b'
},
{
address: '5E9XHcUfeDCL2HEvH8c8rcfroNDSzbLwhV5A1fq7J7RUwAkd',
Expand Down
84 changes: 51 additions & 33 deletions packages/ui/cypress/support/Extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { InjectedAccountWitMnemonic } from '../fixtures/testAccounts'
import { TypeRegistry } from '@polkadot/types'
import { SignerPayloadJSON } from '@polkadot/types/types'
import { cryptoWaitReady } from '@polkadot/util-crypto'
import { SignerResult } from '@polkadot/api/types'

export interface AuthRequest {
id: number
Expand All @@ -29,12 +30,14 @@ export class Extension {
accounts: InjectedAccountWitMnemonic[] = []
txRequests: TxRequests = {}
keyring: Keyring | undefined
allowedOrigins: Record<string, string[]> = {}

reset = () => {
this.authRequests = {}
this.accounts = []
this.txRequests = {}
this.keyring = undefined
this.allowedOrigins = {}
}

init = async (accounts: InjectedAccountWitMnemonic[]) => {
Expand All @@ -51,46 +54,61 @@ export class Extension {
return {
'polkadot-js': {
enable: (origin: string) => {
const resolvedObject = (selectedAccounts: InjectedAccountWitMnemonic[]) => ({
accounts: {
get: () => selectedAccounts,
subscribe: (cb: (accounts: InjectedAccountWitMnemonic[]) => void) =>
cb(selectedAccounts)
} as unknown as InjectedAccounts,
signer: {
signPayload: (payload: SignerPayloadJSON): Promise<SignerResult> => {
return new Promise<SignerResult>((resolve, reject) => {
const id = Date.now()
const res = () => {
const registry = new TypeRegistry()
registry.setSignedExtensions(payload.signedExtensions)
const pair = this.keyring?.getPair(this.accounts[0].address)
if (!pair) {
console.error('Pair not found')
return
}
const signature = registry
.createType('ExtrinsicPayload', payload, {
version: payload.version
})
.sign(pair)
resolve({ id, signature: signature.signature })
}

const rej = (reason: string) => reject(new Error(reason))

this.txRequests[id] = { id, payload, resolve: res, reject: rej }
})
}
}
})

// this origin has already allowed some accounts
if (this.allowedOrigins[origin]?.length) {
// return the list of accounts
const res = resolvedObject(
this.accounts.filter(({ address }) => this.allowedOrigins[origin].includes(address))
)
return Promise.resolve(res)
}

return new Promise<Injected>((resolve, reject) => {
const timestamp = Date.now()
const res = (accountAddresses: string[]) => {
const selectedAccounts = this.accounts.filter(({ address }) =>
accountAddresses.includes(address)
)

resolve({
accounts: {
get: () => selectedAccounts,
subscribe: (cb: (accounts: InjectedAccountWitMnemonic[]) => void) =>
cb(selectedAccounts)
} as unknown as InjectedAccounts,
signer: {
signPayload: (payload: SignerPayloadJSON) => {
return new Promise((resolve, reject) => {
const id = Date.now()
const res = () => {
const registry = new TypeRegistry()
registry.setSignedExtensions(payload.signedExtensions)
const pair = this.keyring?.getPair(this.accounts[0].address)
if (!pair) {
console.error('Pair not found')
return
}
const signature = registry
.createType('ExtrinsicPayload', payload, {
version: payload.version
})
.sign(pair)
resolve({ id, signature: signature.signature })
}

const rej = (reason: string) => reject(new Error(reason))

this.txRequests[id] = { id, payload, resolve: res, reject: rej }
})
}
}
})
// store the allowed accounts for this orgin
this.allowedOrigins[origin] = accountAddresses

const res = resolvedObject(selectedAccounts)
resolve(res)
}

const rej = (reason: string) => reject(new Error(reason))
Expand Down
28 changes: 24 additions & 4 deletions packages/ui/cypress/support/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,16 +45,29 @@ const LOCALSTORAGE_WATCHED_ACCOUNTS_KEY = 'multix.watchedAccount'
// }

const extension = new Extension()
const Account1 = testAccounts['Test Account 1'].address
const Account1 = testAccounts['Multisig Member Account 1'].address

const injectExtension = (win: Cypress.AUTWindow, extension: Extension) => {
Object.defineProperty(win, 'injectedWeb3', {
get: () => extension.getInjectedEnable()
})
}
Cypress.Commands.add('initExtension', (accounts: InjectedAccountWitMnemonic[]) => {
cy.log('Initializing extension')
cy.wrap(extension.init(accounts))

return cy.window().then((win) => {
Object.defineProperty(win, 'injectedWeb3', {
get: () => extension.getInjectedEnable()
})
injectExtension(win, extension)
})
})

Cypress.Commands.add('visitWithInjectedExtension', (url: string) => {
cy.log('Extension enabled')

return cy.visit(url, {
onLoad(win) {
injectExtension(win, extension)
}
})
})

Expand Down Expand Up @@ -129,6 +142,13 @@ declare global {
*/
initExtension: (accounts: InjectedAccountWitMnemonic[]) => Chainable<AUTWindow>

/**
* Visit a page with extension injected. It needs to be initialized first.
* @param {string} url - Page to visit.
* @example cy.visitWithInjectedExtension('http://localhost:3333')
*/
visitWithInjectedExtension: (url: string) => Chainable<AUTWindow>

/**
* Read the authentication request queue.
* @example cy.getAuthRequests().then((authQueue) => { cy.wrap(Object.values(authQueue).length).should("eq", 1) })
Expand Down
2 changes: 2 additions & 0 deletions packages/ui/cypress/support/page-objects/landingPage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ export const landingPage = {
rpcLoader: () => cy.get('[data-cy=loader-rpc-connection]'),
polkadotWikiLink: () => cy.get('[data-cy=link-polkadot-wiki]'),
noAccountFoundError: () => cy.get('[data-cy=label-no-account-found]', { timeout: 10000 }),
linkedAddressNotFound: () => cy.get('[data-cy=label-linked-address-not-found]'),
resetLinkedAddressButton: () => cy.get('[data-cy=button-reset-linked-address]'),

// page specific assertion
shouldHaveNoAccountErrorAndWikiLink() {
Expand Down
6 changes: 5 additions & 1 deletion packages/ui/cypress/support/page-objects/topMenuItems.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,9 @@ export const topMenuItems = {
aboutButton: () => cy.get('[data-cy=button-navigate-about]'),
connectButton: () => cy.get('[data-cy=button-menu-connect]'),
multiproxySelector: () => cy.get('[data-cy=select-multiproxy]', { timeout: 20000 }),
multiproxySelectorOption: () => cy.get('[data-cy=select-multiproxy-option]')
multiproxySelectorInput: () => cy.get('[data-cy=input-select-multiproxy]', { timeout: 10000 }),
multiproxySelectorOption: () => cy.get('[data-cy=select-multiproxy-option]'),
networkSelector: () => cy.get('[data-cy=select-networks]'),
networkSelectorOption: (networkName: string) =>
cy.get(`[data-cy=select-network-option-${networkName}]`)
}
Loading

0 comments on commit 39e7763

Please sign in to comment.