Skip to content

Commit

Permalink
fix: use dataGas when calculating message hash of <1.0.0 (#4621)
Browse files Browse the repository at this point in the history
* fix: don't use `chainId` when calculating domain hash of <=1.2.0

* Extract check to variable

* fix: use `dataGas` when calculating message hash of <1.0.0
  • Loading branch information
iamacook authored Dec 19, 2024
1 parent 9a20297 commit 914fd3b
Show file tree
Hide file tree
Showing 2 changed files with 96 additions and 4 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { faker } from '@faker-js/faker'
import { getDomainHash } from '.'
import { getDomainHash, getMessageHash } from '.'
import { AbiCoder, keccak256 } from 'ethers'
import type { SafeTransactionData, SafeVersion } from '@safe-global/safe-core-sdk-types'

// <= 1.2.0
// keccak256("EIP712Domain(address verifyingContract)");
Expand All @@ -10,6 +11,14 @@ const OLD_DOMAIN_TYPEHASH = '0x035aff83d86937d35b32e04f0ddc6ff469290eef2f1b692d8
// keccak256("EIP712Domain(uint256 chainId,address verifyingContract)");
const NEW_DOMAIN_TYPEHASH = '0x47e79534a245952e8b16893a336b85a3d9ea9fa8c573f3d803afb92a79469218'

// < 1.0.0
// keccak256("SafeTx(address to,uint256 value,bytes data,uint8 operation,uint256 safeTxGas,uint256 dataGas,uint256 gasPrice,address gasToken,address refundReceiver,uint256 nonce)");
const OLD_SAFE_TX_TYPEHASH = '0x14d461bc7412367e924637b363c7bf29b8f47e2f84869f4426e5633d8af47b20'

// >= 1.0.0
// keccak256("SafeTx(address to,uint256 value,bytes data,uint8 operation,uint256 safeTxGas,uint256 baseGas,uint256 gasPrice,address gasToken,address refundReceiver,uint256 nonce)");
const NEW_SAFE_TX_TYPEHASH = '0xbb8310d486368db6bd6f849402fdd73ad53d316b5a4b2644ad6efe0f941286d8'

describe('SafeTxHashDataRow', () => {
describe('getDomainHash', () => {
it.each(['1.0.0' as const, '1.1.1' as const, '1.2.0' as const])(
Expand Down Expand Up @@ -45,4 +54,64 @@ describe('SafeTxHashDataRow', () => {
},
)
})

describe('getMessageHash', () => {
it.each([
['0.1.0' as SafeVersion, OLD_SAFE_TX_TYPEHASH],
['1.0.0' as const, NEW_SAFE_TX_TYPEHASH],
['1.1.1' as const, NEW_SAFE_TX_TYPEHASH],
['1.2.0' as const, NEW_SAFE_TX_TYPEHASH],
['1.3.0' as const, NEW_SAFE_TX_TYPEHASH],
['1.4.1' as const, NEW_SAFE_TX_TYPEHASH],
])('should return the message hash for version %s', (version, typehash) => {
const SafeTx: SafeTransactionData = {
to: faker.finance.ethereumAddress(),
value: faker.string.numeric(),
data: faker.string.hexadecimal({ length: 30 }),
operation: faker.number.int({ min: 0, max: 1 }),
safeTxGas: faker.string.numeric(),
baseGas: faker.string.numeric(), // <1.0.0 is dataGas
gasPrice: faker.string.numeric(),
gasToken: faker.finance.ethereumAddress(),
refundReceiver: faker.finance.ethereumAddress(),
nonce: faker.number.int({ min: 0, max: 69 }),
}

const result = getMessageHash({ safeVersion: version, safeTxData: SafeTx })

expect(result).toEqual(
keccak256(
AbiCoder.defaultAbiCoder().encode(
[
'bytes32',
'address', // to
'uint256', // value
'bytes32', // data
'uint8', // operation
'uint256', // safeTxGas
'uint256', // dataGas/baseGas
'uint256', // gasPrice
'address', // gasToken
'address', // refundReceiver
'uint256', // nonce
],
[
typehash,
SafeTx.to,
SafeTx.value,
// EIP-712 expects data to be hashed
keccak256(SafeTx.data),
SafeTx.operation,
SafeTx.safeTxGas,
SafeTx.baseGas,
SafeTx.gasPrice,
SafeTx.gasToken,
SafeTx.refundReceiver,
SafeTx.nonce,
],
),
),
)
})
})
})
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,31 @@ export function getDomainHash({
})
}

const NEW_SAFE_TX_TYPE_HASH_VERSION = '>=1.0.0'

export function getMessageHash({
safeVersion,
safeTxData,
}: {
safeVersion: SafeVersion
safeTxData: SafeTransactionData
}): string {
const usesBaseGas = semverSatisfies(safeVersion, NEW_SAFE_TX_TYPE_HASH_VERSION)
const SafeTx = getEip712TxTypes(safeVersion).SafeTx

// Clone to not modify the original
const tx: any = { ...safeTxData }

if (!usesBaseGas) {
tx.dataGas = tx.baseGas
delete tx.baseGas

SafeTx[5].name = 'dataGas'
}

return TypedDataEncoder.hashStruct('SafeTx', { SafeTx }, tx)
}

export const SafeTxHashDataRow = ({
safeTxHash,
safeTxData,
Expand All @@ -37,9 +62,7 @@ export const SafeTxHashDataRow = ({
const safeAddress = useSafeAddress()

const domainHash = getDomainHash({ chainId, safeAddress, safeVersion })
const messageHash = safeTxData
? TypedDataEncoder.hashStruct('SafeTx', { SafeTx: getEip712TxTypes(safeVersion).SafeTx }, safeTxData)
: undefined
const messageHash = safeTxData ? getMessageHash({ safeVersion, safeTxData }) : undefined

return (
<>
Expand Down

0 comments on commit 914fd3b

Please sign in to comment.