Skip to content

Commit

Permalink
Merge pull request #94 from gnosisguild/fix-multicall-unfold
Browse files Browse the repository at this point in the history
Fix Uniswap multicall unfold
  • Loading branch information
jfschwarz authored Mar 13, 2024
2 parents 53d7aff + 2f54c8e commit 816a467
Show file tree
Hide file tree
Showing 3 changed files with 52 additions and 18 deletions.
21 changes: 14 additions & 7 deletions extension/src/browser/Drawer/RolePermissionCheck.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,11 @@ import { Flex, Tag } from '../../components'
import { useApplicableTranslation } from '../../transactionTranslations'
import { Connection, JsonRpcError, TransactionData } from '../../types'
import { decodeRolesV1Error } from '../../utils'
import { decodeRolesV2Error, isPermissionsError } from '../../utils/decodeError'
import {
decodeGenericError,
decodeRolesV2Error,
isPermissionsError,
} from '../../utils/decodeError'

import CopyToClipboard from './CopyToClipboard'
import { Translate } from './Translate'
Expand Down Expand Up @@ -42,13 +46,16 @@ const simulateRolesTransaction = async (
? decodeRolesV1Error(e as JsonRpcError)
: decodeRolesV2Error(e as JsonRpcError)

if (!decodedError) {
console.error('Unexpected error', e)
if (decodedError) {
return isPermissionsError(decodedError.signature)
? decodedError.signature
: false
} else {
const genericError = decodeGenericError(e as JsonRpcError)
if (genericError === 'Module not authorized') {
return 'Not a member of any role'
}
}

return decodedError && isPermissionsError(decodedError.signature)
? decodedError.signature
: false
}

return false
Expand Down
18 changes: 18 additions & 0 deletions extension/src/providers/ForkProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ class ForkProvider extends EventEmitter {

private blockGasLimitPromise: Promise<number>

private pendingMetaTransaction: Promise<any> | undefined

constructor(
provider: TenderlyProvider,
{
Expand Down Expand Up @@ -143,6 +145,22 @@ class ForkProvider extends EventEmitter {
* @param metaTx A MetaTransaction object, can be operation: 1 (delegatecall)
*/
async sendMetaTransaction(metaTx: MetaTransaction): Promise<string> {
// If this function is called concurrently we need to serialize the requests so we can take a snapshot in between each call

// If there's a pending request, wait for it to finish before sending the next one
const send = this.pendingMetaTransaction
? async () => {
await this.pendingMetaTransaction
return await this._sendMetaTransaction(metaTx)
}
: async () => await this._sendMetaTransaction(metaTx)

// Synchronously update `this.pendingMetaTransaction` so subsequent `sendMetaTransaction()` calls will go to the back of the queue
this.pendingMetaTransaction = send()
return await this.pendingMetaTransaction
}

private async _sendMetaTransaction(metaTx: MetaTransaction): Promise<string> {
const isDelegateCall = metaTx.operation === 1
if (isDelegateCall && !this.moduleAddress && !this.ownerAddress) {
throw new Error('delegatecall requires moduleAddress or ownerAddress')
Expand Down
31 changes: 20 additions & 11 deletions extension/src/utils/decodeError.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { defaultAbiCoder } from '@ethersproject/abi'
import { ContractFactories, KnownContracts } from '@gnosis.pm/zodiac'
import { JsonRpcError } from '../types'

Expand All @@ -14,7 +15,12 @@ export function getRevertData(error: JsonRpcError) {
// - RPC provider (Infura vs. Alchemy)
// - client library (ethers vs. directly using the EIP-1193 provider)

// Here we try to fix the revert reason in any of the possible formats
// first, drill through potential error wrappings down to the original error
while (typeof error === 'object' && (error as any).error) {
error = (error as any).error
}

// Here we try to extract the revert reason in any of the possible formats
const message =
error.data?.originalError?.data ||
error.data?.data ||
Expand All @@ -30,9 +36,20 @@ export function getRevertData(error: JsonRpcError) {

export function decodeGenericError(error: JsonRpcError) {
const revertData = getRevertData(error)
if (revertData.startsWith('0x')) {
return asciiDecode(revertData.substring(2))

// Solidity `revert "reason string"` will revert with the data encoded as selector of `Error(string)` followed by the ABI encoded string param
if (revertData.startsWith('0x08c379a0')) {
try {
const [reason] = defaultAbiCoder.decode(
['string'],
'0x' + revertData.slice(10) // skip over selector
)
return reason as string
} catch (e) {
return revertData
}
}

return revertData
}

Expand Down Expand Up @@ -92,11 +109,3 @@ const PERMISSION_ERRORS = Object.keys(RolesV1Interface.errors)
export const isPermissionsError = (errorSignature: string) =>
PERMISSION_ERRORS.includes(errorSignature) &&
errorSignature !== 'ModuleTransactionFailed()'

function asciiDecode(hex: string) {
let result = ''
for (let i = 0; i < hex.length; i += 2) {
result += String.fromCharCode(parseInt(hex.substring(i, i + 2), 16))
}
return result
}

0 comments on commit 816a467

Please sign in to comment.