Skip to content

Commit

Permalink
successfully recover cip64 transactions
Browse files Browse the repository at this point in the history
  • Loading branch information
aaronmgdr committed Sep 21, 2023
1 parent 9dc0002 commit 02573df
Show file tree
Hide file tree
Showing 4 changed files with 91 additions and 13 deletions.
26 changes: 26 additions & 0 deletions packages/sdk/wallets/wallet-base/src/signing-utils.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -369,6 +369,32 @@ describe('recoverTransaction', () => {
]
`)
})
it('handles cip64 transactions', () => {
const cip64TX =
'0x7bf88282ad5a8063630a94588e4b68193001e4d10928660ab4165b813717c0880de0b6b3a764000083abcdefc094cd2a3d9f938e13cd947ec05abc7fe734df8dd82680a091b5504a59e529e7efa42dbb97fbc3311a91d035c873a94ab0789441fc989f84a02e8254d6b3101b63417e5d496833bc84f4832d4a8bf8a2b83e291d8f38c0f62d'
expect(recoverTransaction(cip64TX)).toMatchInlineSnapshot(`
[
{
"accessList": [],
"chainId": 44378,
"data": "0xabcdef",
"feeCurrency": "0xcd2a3d9f938e13cd947ec05abc7fe734df8dd826",
"gas": 10,
"maxFeePerGas": 99,
"maxPriorityFeePerGas": 99,
"nonce": 0,
"r": "0x91b5504a59e529e7efa42dbb97fbc3311a91d035c873a94ab0789441fc989f84",
"s": "0x2e8254d6b3101b63417e5d496833bc84f4832d4a8bf8a2b83e291d8f38c0f62d",
"to": "0x588e4b68193001e4d10928660ab4165b813717c0",
"type": "cip64",
"v": 27,
"value": 1000000000000000000,
"yParity": 0,
},
"0xb2a81DC4204cF3E96488dFa71a633ae5B336b3fE",
]
`)
})
it('handles cip42 transactions', () => {
const cip42TX =
'0x7cf89a82ad5a8063630a94cd2a3d9f938e13cd947ec05abc7fe734df8dd826941be31a94361a391bbafb2a4ccd704f57dc04d4bb82567894588e4b68193001e4d10928660ab4165b813717c0880de0b6b3a764000083abcdefc01ba0c610507b2ac3cff80dd7017419021196807d605efce0970c18cde48db33c27d1a01799477e0f601f554f0ee6f7ac21490602124801e9f7a99d9605249b90f03112'
Expand Down
22 changes: 14 additions & 8 deletions packages/sdk/wallets/wallet-base/src/signing-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -387,10 +387,13 @@ function prefixAwareRLPDecode(rlpEncode: string, type: TransactionTypes): string
return type === 'celo-legacy' ? RLP.decode(rlpEncode) : RLP.decode(`0x${rlpEncode.slice(4)}`)
}

function correctLengthWithSignatureOf(type: TransactionTypes) {
function correctLengthOf(type: TransactionTypes, hasSignature: boolean = true) {
switch (type) {
case 'cip64': {
return hasSignature ? 13 : 10
}
case 'cip42':
return 15
return hasSignature ? 15 : 12
case 'celo-legacy':
case 'eip1559':
return 12
Expand All @@ -401,9 +404,9 @@ export function extractSignature(rawTx: string) {
const type = determineTXType(rawTx)
const rawValues = prefixAwareRLPDecode(rawTx, type)
const length = rawValues.length
if (correctLengthWithSignatureOf(type) !== length) {
if (correctLengthOf(type) !== length) {
throw new Error(
`@extractSignature: provided transaction has ${length} elements but ${type} txs with a signature have ${correctLengthWithSignatureOf(
`@extractSignature: provided transaction has ${length} elements but ${type} txs with a signature have ${correctLengthOf(
type
)} ${JSON.stringify(rawValues)}`
)
Expand Down Expand Up @@ -473,9 +476,9 @@ export function recoverTransaction(rawTx: string): [CeloTx, string] {
}

// inspired by @ethereumjs/tx
function getPublicKeyofSignerFromTx(transactionArray: string[]) {
// TODO this needs to be 10 for cip64
const base = transactionArray.slice(0, 12) // 12 is length of cip42 without vrs fields
function getPublicKeyofSignerFromTx(transactionArray: string[], type: TransactionTypes) {
// this needs to be 10 for cip64, 12 for cip42 and eip1559
const base = transactionArray.slice(0, correctLengthOf(type, false))
const message = concatHex([TxTypeToPrefix.cip42, RLP.encode(base).slice(2)])
const msgHash = keccak256(hexToBytes(message))

Expand All @@ -494,7 +497,10 @@ function getPublicKeyofSignerFromTx(transactionArray: string[]) {

export function getSignerFromTxEIP2718TX(serializedTransaction: string): string {
const transactionArray: any[] = RLP.decode(`0x${serializedTransaction.slice(4)}`)
const signer = getPublicKeyofSignerFromTx(transactionArray)
const signer = getPublicKeyofSignerFromTx(
transactionArray,
determineTXType(serializedTransaction)
)
return toChecksumAddress(Address.fromPublicKey(signer).toString())
}

Expand Down
53 changes: 49 additions & 4 deletions packages/sdk/wallets/wallet-local/src/local-wallet.test.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { StrongAddress } from '@celo/base/lib/address'
import { CeloTx, EncodedTransaction, Hex } from '@celo/connect'
import {
normalizeAddressWith0x,
Expand All @@ -12,7 +13,6 @@ import { TransactionSerializableEIP1559, parseTransaction } from 'viem'
import { privateKeyToAccount } from 'viem/accounts'
import Web3 from 'web3'
import { LocalWallet } from './local-wallet'
import { StrongAddress } from '@celo/base/lib/address'

const CHAIN_ID = 44378

Expand Down Expand Up @@ -272,13 +272,43 @@ describe('Local wallet class', () => {
)
expect(signedTransaction.raw).toEqual(viemSignedTransaction)
})
test('succeeds with cip64', async () => {
const recoverTransactionCIP64 = {
...celoTransactionWithGasPrice,
gasPrice: undefined,
gatewayFee: undefined,
gatewayFeeRecipient: undefined,
maxFeePerGas: '99',
maxPriorityFeePerGas: '99',
feeCurrency: '0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826',
}
await expect(wallet.signTransaction(recoverTransactionCIP64)).resolves
.toMatchInlineSnapshot(`
{
"raw": "0x7bf88282ad5a8063630a94588e4b68193001e4d10928660ab4165b813717c0880de0b6b3a764000083abcdefc094cd2a3d9f938e13cd947ec05abc7fe734df8dd82680a091b5504a59e529e7efa42dbb97fbc3311a91d035c873a94ab0789441fc989f84a02e8254d6b3101b63417e5d496833bc84f4832d4a8bf8a2b83e291d8f38c0f62d",
"tx": {
"gas": "0x0a",
"hash": "0x645afc1d19fe805c0c0956e70d5415487bf073741d7b297ccb7e7040c6ce5df6",
"input": "0xabcdef",
"nonce": "0",
"r": "0x91b5504a59e529e7efa42dbb97fbc3311a91d035c873a94ab0789441fc989f84",
"s": "0x2e8254d6b3101b63417e5d496833bc84f4832d4a8bf8a2b83e291d8f38c0f62d",
"to": "0x588e4b68193001e4d10928660ab4165b813717c0",
"v": "0x",
"value": "0x0de0b6b3a7640000",
},
"type": "cip64",
}
`)
})

test('succeeds with cip42', async () => {
const transaction42 = {
...celoTransactionWithGasPrice,
gasPrice: undefined,
maxFeePerGas: '99',
maxPriorityFeePerGas: '99',
gatewayFee: '0x5678',
feeCurrency: '0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826',
}
await expect(wallet.signTransaction(transaction42)).resolves.toMatchInlineSnapshot(`
Expand Down Expand Up @@ -343,7 +373,7 @@ describe('Local wallet class', () => {
)
})
})
describe('when using signTransaction with type CIP42', () => {
describe('when using signTransaction with type CIP42/64', () => {
let celoTransactionBase: CeloTx
let feeCurrency = '0x10c892a6ec43a53e45d0b916b4b7d383b1b78c0f'
let maxFeePerGas = '0x100000000'
Expand All @@ -360,11 +390,26 @@ describe('Local wallet class', () => {
data: '0xabcdef',
}
})

describe('when feeCurrency and maxPriorityFeePerGas and maxFeePerGas are set', () => {
describe('when feeCurrency and maxPriorityFeePerGas and maxFeePerGas are set but no gatewayfees', () => {
it('signs as a CIP64 tx', async () => {
const transaction: CeloTx = {
...celoTransactionBase,
gatewayFee: undefined,
gatewayFeeRecipient: undefined,
feeCurrency,
maxFeePerGas,
maxPriorityFeePerGas,
}
const signedTx: EncodedTransaction = await wallet.signTransaction(transaction)
expect(signedTx.raw).toMatch(/^0x7b/)
})
})
describe('when feeCurrency and gatewayFee and maxPriorityFeePerGas and maxFeePerGas are set', () => {
it('signs as a CIP42 tx', async () => {
const transaction: CeloTx = {
...celoTransactionBase,
gatewayFee: '0x1331',
gatewayFeeRecipient: FEE_ADDRESS,
feeCurrency,
maxFeePerGas,
maxPriorityFeePerGas,
Expand Down
3 changes: 2 additions & 1 deletion packages/sdk/wallets/wallet-local/src/signing.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -182,11 +182,12 @@ describe('Transaction Utils', () => {
if (celoTransaction.gasPrice != undefined) {
description.push(`Testing Legacy with gas price ${celoTransaction.gasPrice}`)
} else if (
celoTransaction.feeCurrency != undefined ||
celoTransaction.gatewayFeeRecipient !== undefined ||
celoTransaction.gatewayFee !== undefined
) {
description.push('Testing CIP42 with')
} else if (celoTransaction.feeCurrency != undefined) {
description.push('Testing CIP64 with')
} else {
description.push(`Testing EIP1559 with maxFeePerGas ${celoTransaction.maxFeePerGas}`)
}
Expand Down

0 comments on commit 02573df

Please sign in to comment.