Skip to content
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

chore: New release #730

Merged
merged 7 commits into from
Jul 10, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions apps/wallet-connect/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@
"private": true,
"homepage": "./",
"dependencies": {
"@safe-global/safe-apps-provider": "0.17.0",
"@gnosis.pm/safe-react-components": "^0.9.7",
"@safe-global/safe-apps-provider": "0.17.0",
"@safe-global/safe-gateway-typescript-sdk": "^3.7.3",
"@walletconnect/client": "^1.8.0",
"@walletconnect/web3wallet": "^1.8.0",
"@walletconnect/web3wallet": "^1.8.6",
"date-fns": "^2.30.0",
"ethers": "^5.7.2",
"jsqr": "^1.4.0"
Expand Down
57 changes: 39 additions & 18 deletions apps/wallet-connect/src/App.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import {
mockValidTransactionRequest,
} from './mocks/mocks'
import { renderWithProviders } from './utils/test-helpers'
import { compatibleSafeMethods } from './hooks/useWalletConnectV2'

const CONNECTION_INPUT_TEXT = 'QR code or connection link'
const HELP_TITLE = 'How to connect to a Dapp?'
Expand All @@ -36,7 +37,7 @@ const version2URI =
'wc:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx@2?relay-protocol=irn&symKey=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'

const invalidConnectionErrorLabel =
'Connection refused: Incompatible chain detected. Make sure the Dapp only uses Goerli to interact with this Safe.'
'Connection refused: the dApp you are using is sending a connection proposal that is incompatible with your Safe Account'

jest.mock('@safe-global/safe-gateway-typescript-sdk', () => {
return {
Expand Down Expand Up @@ -384,15 +385,17 @@ describe('Walletconnect unit tests', () => {
expect(screen.queryByText(invalidConnectionErrorLabel)).not.toBeInTheDocument()

const safeAccount = [`eip155:${mockSafeInfo.chainId}:${mockSafeInfo.safeAddress}`]
const safeChain = [`eip155:${mockSafeInfo.chainId}`]

// approved session is sent
expect(mockApproveSession).toBeCalledWith({
id: mockSessionProposal.id,
namespaces: {
eip155: {
accounts: safeAccount,
methods: mockSessionProposal.params.requiredNamespaces.eip155.methods,
methods: compatibleSafeMethods,
events: mockSessionProposal.params.requiredNamespaces.eip155.events,
chains: safeChain,
},
},
})
Expand All @@ -410,7 +413,7 @@ describe('Walletconnect unit tests', () => {
})

mockApproveSession.mockImplementation(() => {
return Promise.resolve(mockV2SessionObj)
return Promise.reject()
})

renderWithProviders(<WalletconnectSafeApp />)
Expand All @@ -431,15 +434,23 @@ describe('Walletconnect unit tests', () => {
expect(screen.getByText(invalidConnectionErrorLabel)).toBeInTheDocument(),
)

expect(mockApproveSession).not.toBeCalled()
expect(mockRejectSession).toBeCalledWith({
id: mockInvalidEVMSessionProposal.id,
reason: {
code: 5100,
message:
'Unsupported chains. No EVM-based (eip155) namespace present in the session proposal',
const safeAccount = [`eip155:${mockSafeInfo.chainId}:${mockSafeInfo.safeAddress}`]
const safeChain = [`eip155:${mockSafeInfo.chainId}`]

// we try to connect
expect(mockApproveSession).toHaveBeenCalledWith({
id: 1111111111111111,
namespaces: {
eip155: {
accounts: safeAccount,
chains: safeChain,
events: [],
methods: compatibleSafeMethods,
},
},
})

expect(mockRejectSession).toBeCalled()
})

it('rejects session proposals without Safe chain', async () => {
Expand All @@ -454,7 +465,9 @@ describe('Walletconnect unit tests', () => {
})

mockApproveSession.mockImplementation(() => {
return Promise.resolve(mockV2SessionObj)
return Promise.reject({
message: 'chains', // wallet connect rejects the connection now
})
})

renderWithProviders(<WalletconnectSafeApp />)
Expand All @@ -475,15 +488,23 @@ describe('Walletconnect unit tests', () => {
expect(screen.getByText(invalidConnectionErrorLabel)).toBeInTheDocument(),
)

expect(mockApproveSession).not.toBeCalled()
expect(mockRejectSession).toBeCalledWith({
id: mockInvalidEVMSessionProposal.id,
reason: {
code: 5100,
message:
'Unsupported chains. No Goerli (eip155:5) namespace present in the session proposal',
const safeAccount = [`eip155:${mockSafeInfo.chainId}:${mockSafeInfo.safeAddress}`]
const safeChain = [`eip155:${mockSafeInfo.chainId}`]

// we try to connect
expect(mockApproveSession).toHaveBeenCalledWith({
id: 1111111111111111,
namespaces: {
eip155: {
accounts: safeAccount,
chains: safeChain,
events: [],
methods: compatibleSafeMethods,
},
},
})

expect(mockRejectSession).toBeCalled()
})
})

Expand Down
Binary file modified apps/wallet-connect/src/assets/cam-permissions.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 4 additions & 1 deletion apps/wallet-connect/src/hooks/useApps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,10 @@ export function useApps(): UseAppsResponse {
const openSafeApp = useCallback(
(url: string) => {
if (origin?.length) {
window.open(`${origin}/${networkPrefix}:${safe.safeAddress}/apps?appUrl=${url}`, '_blank')
window.open(
`${origin}/apps/open?safe=${networkPrefix}:${safe.safeAddress}&appUrl=${url}`,
'_blank',
)
}
},
[networkPrefix, origin, safe],
Expand Down
104 changes: 24 additions & 80 deletions apps/wallet-connect/src/hooks/useWalletConnectV2.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import { isProduction, SAFE_WALLET_METADATA, WALLETCONNECT_V2_PROJECT_ID } from
const EVMBasedNamespaces: string = 'eip155'

// see full list here: https://github.com/safe-global/safe-apps-sdk/blob/main/packages/safe-apps-provider/src/provider.ts#L35
const compatibleSafeMethods: string[] = [
export const compatibleSafeMethods: string[] = [
'eth_accounts',
'net_version',
'eth_chainId',
Expand Down Expand Up @@ -53,6 +53,9 @@ const USER_DISCONNECTED_CODE = 6000

const logger = isProduction ? undefined : 'debug'

export const errorLabel =
'Connection refused: the dApp you are using is sending a connection proposal that is incompatible with your Safe Account'

export type wcConnectType = (uri: string) => Promise<void>
export type wcDisconnectType = () => Promise<void>

Expand Down Expand Up @@ -196,76 +199,23 @@ const useWalletConnectV2 = (
// events
web3wallet.on('session_proposal', async proposal => {
const { id, params } = proposal
const { requiredNamespaces, optionalNamespaces } = params

const requiredEIP155Namespace = requiredNamespaces[EVMBasedNamespaces]
const optionalEIP155Namespace = optionalNamespaces[EVMBasedNamespaces]
const { requiredNamespaces } = params

console.log('Session proposal: ', proposal)

// EVM-based (eip155) namespace should be present
const isEIP155NamespacePresent = !!requiredEIP155Namespace

if (!isEIP155NamespacePresent) {
const errorMessage = getConnectionErrorMessage('chains error', chainInfo?.chainName)
setError(errorMessage)

await web3wallet.rejectSession({
id: proposal.id,
reason: {
code: UNSUPPORTED_CHAIN_ERROR_CODE,
message: `Unsupported chains. No EVM-based (${EVMBasedNamespaces}) namespace present in the session proposal`,
},
})
return
}

// Safe chain should be present
const isSafeChainIdPresent = requiredEIP155Namespace.chains?.some(
chain => chain === `${EVMBasedNamespaces}:${safe.chainId}`,
)

if (!isSafeChainIdPresent) {
const errorMessage = getConnectionErrorMessage('chains error', chainInfo?.chainName)
setError(errorMessage)

await web3wallet.rejectSession({
id: proposal.id,
reason: {
code: UNSUPPORTED_CHAIN_ERROR_CODE,
message: `Unsupported chains. No ${chainInfo?.chainName} (${EVMBasedNamespaces}:${safe.chainId}) namespace present in the session proposal`,
},
})
return
}

// we only accept methods compatible with the Safe
const requiredCompatibleMethods =
requiredEIP155Namespace?.methods.filter(method =>
compatibleSafeMethods.includes(method),
) || []

// we only accept methods compatible with the Safe
const optionalCompatibleMethods =
optionalEIP155Namespace?.methods.filter(method =>
compatibleSafeMethods.includes(method),
) || []

const compatibleSafeAccountMethods = [
...requiredCompatibleMethods,
...optionalCompatibleMethods,
]

const safeAccount = `${EVMBasedNamespaces}:${safe.chainId}:${safe.safeAddress}`
const safeChain = `${EVMBasedNamespaces}:${safe.chainId}`
const safeEvents = requiredNamespaces[EVMBasedNamespaces]?.events || [] // we accept all events like chainChanged & accountsChanged (even if they are not compatible with the Safe)

try {
const wcSession = await web3wallet.approveSession({
id,
namespaces: {
eip155: {
accounts: [safeAccount], // only the Safe Account
methods: compatibleSafeAccountMethods, // only methods compatible with the Safe
events: requiredEIP155Namespace.events, // we accept all events like chainChanged & accountsChanged (even if they are not compatible with the Safe)
accounts: [safeAccount], // only the Safe account
chains: [safeChain], // only the Safe chain
methods: compatibleSafeMethods, // only the Safe methods
events: safeEvents,
},
},
})
Expand All @@ -276,9 +226,19 @@ const useWalletConnectV2 = (
setError(undefined)
} catch (error: any) {
console.log('error: ', error)
console.log('error: ', error.message)
const errorMessage = getConnectionErrorMessage(error.message, chainInfo?.chainName)
setError(errorMessage)

// human readeable error
setError(errorLabel)

const errorMessage = `Connection refused: This Safe Account is in ${chainInfo?.chainName} but the Wallet Connect session proposal is not valid because it contains: 1) A required chain different than ${chainInfo?.chainName} 2) Does not include ${chainInfo?.chainName} between the optional chains 3) No EVM compatible chain is included`
console.log(errorMessage)
await web3wallet.rejectSession({
id: proposal.id,
reason: {
code: UNSUPPORTED_CHAIN_ERROR_CODE,
message: errorMessage,
},
})
}
})

Expand Down Expand Up @@ -333,19 +293,3 @@ const rejectResponse = (id: number, code: number, message: string) => {
},
}
}

const getConnectionErrorMessage = (errorMessage = '', chainName = ''): string => {
const isChainError = errorMessage.includes('chains')

if (isChainError) {
return `Connection refused: Incompatible chain detected. Make sure the Dapp only uses ${chainName} to interact with this Safe.`
}

const isMethodError = errorMessage.includes('methods')

if (isMethodError) {
return 'Connection refused: Incompatible methods between the Dapp and the Safe Account detected.'
}

return errorMessage
}
Loading
Loading