Skip to content

Commit

Permalink
Merge pull request #1046 from AmbireTech/enhancement/switch-account-a…
Browse files Browse the repository at this point in the history
…ction

Enhancement/ Switch account action
  • Loading branch information
PetromirDev authored Nov 27, 2024
2 parents df6d104 + 96da88e commit 5b76987
Show file tree
Hide file tree
Showing 6 changed files with 143 additions and 29 deletions.
8 changes: 8 additions & 0 deletions src/consts/dappCommunication.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
const ORIGINS_WHITELISTED_TO_ALL_ACCOUNTS = [
'https://legends.ambire.com',
'https://legends-staging.ambire.com',
'http://localhost:19006',
'http://localhost:19007'
]

export { ORIGINS_WHITELISTED_TO_ALL_ACCOUNTS }
18 changes: 16 additions & 2 deletions src/controllers/actions/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ import {
Action,
BenzinAction,
DappRequestAction,
SignMessageAction
SignMessageAction,
SwitchAccountAction
} from '../../interfaces/actions'
import { NotificationManager } from '../../interfaces/notification'
import { WindowManager } from '../../interfaces/window'
Expand All @@ -20,7 +21,14 @@ import EventEmitter from '../eventEmitter/eventEmitter'
import { SelectedAccountController } from '../selectedAccount/selectedAccount'

// TODO: Temporarily. Refactor imports across the codebase to ref /interfaces/actions instead.
export type { Action, AccountOpAction, SignMessageAction, BenzinAction, DappRequestAction }
export type {
SwitchAccountAction,
Action,
AccountOpAction,
SignMessageAction,
BenzinAction,
DappRequestAction
}

/**
* The ActionsController is responsible for storing the converted userRequests
Expand Down Expand Up @@ -74,6 +82,9 @@ export class ActionsController extends EventEmitter {
if (a.type === 'benzin') {
return a.userRequest.meta.accountAddr === this.#selectedAccount.account?.addr
}
if (a.type === 'switchAccount') {
return a.userRequest.meta.switchToAccountAddr !== this.#selectedAccount.account?.addr
}

return true
})
Expand Down Expand Up @@ -274,6 +285,9 @@ export class ActionsController extends EventEmitter {
if (a.type === 'benzin') {
return a.userRequest.meta.accountAddr !== address
}
if (a.type === 'switchAccount') {
return a.userRequest.meta.switchToAccountAddr !== address
}

return true
})
Expand Down
84 changes: 59 additions & 25 deletions src/controllers/main/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { getAddress, getBigInt, Interface, isAddress } from 'ethers'
import AmbireAccount from '../../../contracts/compiled/AmbireAccount.json'
import AmbireFactory from '../../../contracts/compiled/AmbireFactory.json'
import EmittableError from '../../classes/EmittableError'
import { ORIGINS_WHITELISTED_TO_ALL_ACCOUNTS } from '../../consts/dappCommunication'
import { AMBIRE_ACCOUNT_FACTORY, SINGLETON } from '../../consts/deploy'
import {
BIP44_LEDGER_DERIVATION_TEMPLATE,
Expand Down Expand Up @@ -55,6 +56,7 @@ import { GasRecommendation, getGasPriceRecommendations } from '../../libs/gasPri
import { humanizeAccountOp } from '../../libs/humanizer'
import { KeyIterator } from '../../libs/keyIterator/keyIterator'
import {
buildSwitchAccountUserRequest,
getAccountOpsForSimulation,
makeBasicAccountOpAction,
makeSmartAccountOpAction
Expand Down Expand Up @@ -952,6 +954,21 @@ export class MainController extends EventEmitter {
)
}

#getUserRequestAccountError(dappOrigin: string, fromAccountAddr: string): string | null {
if (ORIGINS_WHITELISTED_TO_ALL_ACCOUNTS.includes(dappOrigin)) {
const isAddressInAccounts = this.accounts.accounts.some((a) => a.addr === fromAccountAddr)

if (isAddressInAccounts) return null

return 'The dApp is trying to sign using an address that is not imported in the extension.'
}
const isAddressSelected = this.selectedAccount.account?.addr === fromAccountAddr

if (isAddressSelected) return null

return 'The dApp is trying to sign using an address that is not selected in the extension.'
}

async buildUserRequestFromDAppRequest(
request: DappProviderRequest,
dappPromise: {
Expand Down Expand Up @@ -1012,17 +1029,6 @@ export class MainController extends EventEmitter {
throw ethErrors.rpc.invalidRequest('No msg request to sign')
}
const msgAddress = getAddress(msg?.[1])
// TODO: if address is in this.accounts in theory the user should be able to sign
// e.g. if an acc from the wallet is used as a signer of another wallet
if (msgAddress !== this.selectedAccount.account.addr) {
dappPromise.reject(
ethErrors.provider.userRejectedRequest(
// if updating, check https://github.com/AmbireTech/ambire-wallet/pull/1627
'the dApp is trying to sign using an address different from the currently selected account. Try re-connecting.'
)
)
return
}

const network = this.networks.networks.find(
(n) => Number(n.chainId) === Number(dapp?.chainId)
Expand Down Expand Up @@ -1054,17 +1060,6 @@ export class MainController extends EventEmitter {
throw ethErrors.rpc.invalidRequest('No msg request to sign')
}
const msgAddress = getAddress(msg?.[0])
// TODO: if address is in this.accounts in theory the user should be able to sign
// e.g. if an acc from the wallet is used as a signer of another wallet
if (msgAddress !== this.selectedAccount.account.addr) {
dappPromise.reject(
ethErrors.provider.userRejectedRequest(
// if updating, check https://github.com/AmbireTech/ambire-wallet/pull/1627
'the dApp is trying to sign using an address different from the currently selected account. Try re-connecting.'
)
)
return
}

const network = this.networks.networks.find(
(n) => Number(n.chainId) === Number(dapp?.chainId)
Expand Down Expand Up @@ -1130,10 +1125,47 @@ export class MainController extends EventEmitter {
}
}

if (userRequest) {
if (!userRequest) return

const isASignOperationRequestedForAnotherAccount =
userRequest.meta.isSignAction &&
userRequest.meta.accountAddr !== this.selectedAccount.account?.addr

// We can simply add the user request if it's not a sign operation
// for another account
if (!isASignOperationRequestedForAnotherAccount) {
await this.addUserRequest(userRequest, withPriority)
this.emitUpdate()
return
}

const accountError = this.#getUserRequestAccountError(
dappPromise.session.origin,
userRequest.meta.accountAddr
)

if (accountError) {
dappPromise.reject(ethErrors.provider.userRejectedRequest(accountError))
return
}

const network = this.networks.networks.find((n) => Number(n.chainId) === Number(dapp?.chainId))

if (!network) {
throw ethErrors.provider.chainDisconnected('Transaction failed - unknown network')
}

await this.addUserRequest(
buildSwitchAccountUserRequest({
nextUserRequest: userRequest,
networkId: network.id,
selectedAccountAddr: userRequest.meta.accountAddr,
session: dappPromise.session,
rejectUserRequest: this.rejectUserRequest.bind(this)
}),
true,
'open'
)
await this.addUserRequest(userRequest, false, 'queue')
}

async buildTransferUserRequest(
Expand Down Expand Up @@ -1421,7 +1453,7 @@ export class MainController extends EventEmitter {
this.actions.addOrUpdateAction(accountOpAction, withPriority, executionType)
}
} else {
let actionType: 'dappRequest' | 'benzin' | 'signMessage' = 'dappRequest'
let actionType: 'dappRequest' | 'benzin' | 'signMessage' | 'switchAccount' = 'dappRequest'

if (req.action.kind === 'typedMessage' || req.action.kind === 'message') {
actionType = 'signMessage'
Expand All @@ -1441,6 +1473,8 @@ export class MainController extends EventEmitter {
}
}
if (req.action.kind === 'benzin') actionType = 'benzin'
if (req.action.kind === 'switchAccount') actionType = 'switchAccount'

this.actions.addOrUpdateAction(
{
id,
Expand Down
19 changes: 18 additions & 1 deletion src/interfaces/actions.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,18 @@
import { AccountOp } from '../libs/accountOp/accountOp'
import { Account } from './account'
import { DappUserRequest, SignUserRequest, UserRequest } from './userRequest'

export type SwitchAccountAction = {
id: UserRequest['id']
type: 'switchAccount'
userRequest: {
meta: {
accountAddr: Account['addr']
switchToAccountAddr: Account['addr']
}
}
}

export type AccountOpAction = {
id: SignUserRequest['id']
type: 'accountOp'
Expand All @@ -25,4 +37,9 @@ export type DappRequestAction = {
userRequest: DappUserRequest
}

export type Action = AccountOpAction | SignMessageAction | BenzinAction | DappRequestAction
export type Action =
| SwitchAccountAction
| AccountOpAction
| SignMessageAction
| BenzinAction
| DappRequestAction
2 changes: 1 addition & 1 deletion src/interfaces/userRequest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ export interface SignUserRequest {
export interface DappUserRequest {
id: string | number
action: {
kind: Exclude<string, 'calls' | 'message' | 'typedMessage' | 'benzin'>
kind: Exclude<string, 'calls' | 'message' | 'typedMessage' | 'benzin' | 'switchAccount'>
params: any
}
session: DappProviderRequest['session']
Expand Down
41 changes: 41 additions & 0 deletions src/libs/main/main.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { AccountOpAction, Action } from '../../controllers/actions/actions'
import { Account, AccountId } from '../../interfaces/account'
import { DappProviderRequest } from '../../interfaces/dapp'
import { Network, NetworkId } from '../../interfaces/network'
import { Calls, SignUserRequest, UserRequest } from '../../interfaces/userRequest'
import generateSpoofSig from '../../utils/generateSpoofSig'
Expand Down Expand Up @@ -30,6 +31,46 @@ export const batchCallsFromUserRequests = ({
)
}

export const buildSwitchAccountUserRequest = ({
nextUserRequest,
selectedAccountAddr,
networkId,
session,
rejectUserRequest
}: {
nextUserRequest: UserRequest
selectedAccountAddr: string
networkId: Network['id']
session: DappProviderRequest['session']
rejectUserRequest: (reason: string, userRequestId: string | number) => void
}): UserRequest => {
const userRequestId = nextUserRequest.id

return {
id: Number(nextUserRequest.id) + 222, // Otherwise the ids will be the same
action: {
kind: 'switchAccount',
params: {
accountAddr: selectedAccountAddr,
switchToAccountAddr: nextUserRequest.meta.accountAddr,
nextRequestType: nextUserRequest.action.kind,
networkId
}
},
session,
meta: {
isSignAction: false
},
dappPromise: {
session,
resolve: () => {},
reject: () => {
rejectUserRequest('Switch account request rejected', userRequestId)
}
}
}
}

export const makeSmartAccountOpAction = ({
account,
networkId,
Expand Down

0 comments on commit 5b76987

Please sign in to comment.