-
-
Notifications
You must be signed in to change notification settings - Fork 935
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
add parseTransactionCelo for parsing out serialized transaction types…
… specific to celo chain.
- Loading branch information
Showing
6 changed files
with
361 additions
and
10 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
--- | ||
"viem": minor | ||
--- | ||
|
||
Added `parseTransactionCelo` function for parsing serialized transactions that could be CIP42 tx or other serialized transaction types exported at viem/chains/utils |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,226 @@ | ||
import { accounts } from '../../_test/constants.js' | ||
import { | ||
parseEther, | ||
parseGwei, | ||
parseTransaction as parseTransaction_, | ||
serializeTransaction, | ||
toRlp, | ||
} from '../../index.js' | ||
|
||
import { | ||
type TransactionSerializableCIP42, | ||
serializeTransactionCelo, | ||
} from './serializers.js' | ||
|
||
import { parseTransactionCelo } from './parsers.js' | ||
import { describe, expect, test } from 'vitest' | ||
|
||
describe('parseTransaction', () => { | ||
test('should be able to parse a cip42 transaction', () => { | ||
const signedTransaction = | ||
'0x7cf84682a4ec80847735940084773594008094765de816845861e75a25fca122bb6898b8b1282a808094f39fd6e51aad88f6f4ce6ab8827279cfffb92266880de0b6b3a764000080c0' | ||
|
||
expect(parseTransactionCelo(signedTransaction)).toMatchInlineSnapshot(` | ||
{ | ||
"chainId": 42220, | ||
"feeCurrency": "0x765de816845861e75a25fca122bb6898b8b1282a", | ||
"maxFeePerGas": 2000000000n, | ||
"maxPriorityFeePerGas": 2000000000n, | ||
"to": "0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266", | ||
"type": "cip42", | ||
"value": 1000000000000000000n, | ||
} | ||
`) | ||
}) | ||
|
||
const transaction = { | ||
chainId: 1, | ||
gas: 21001n, | ||
maxFeePerGas: parseGwei('2'), | ||
maxPriorityFeePerGas: parseGwei('2'), | ||
to: accounts[3].address, | ||
nonce: 785, | ||
value: parseEther('1'), | ||
} | ||
|
||
test('should return same result as standard parser when not CIP42', () => { | ||
const serialized = serializeTransaction(transaction) | ||
|
||
expect(parseTransactionCelo(serialized)).toEqual( | ||
parseTransaction_(serialized), | ||
) | ||
}) | ||
|
||
test('should parse a CIP42 transaction with gatewayFee', () => { | ||
const transactionWithGatewayFee = { | ||
...transaction, | ||
chainId: 42270, | ||
gatewayFee: parseEther('0.1'), | ||
gatewayFeeRecipient: accounts[1].address, | ||
} | ||
|
||
const serialized = serializeTransactionCelo(transactionWithGatewayFee) | ||
|
||
expect(parseTransactionCelo(serialized)).toMatchInlineSnapshot(` | ||
{ | ||
"chainId": 42270, | ||
"gas": 21001n, | ||
"gatewayFee": 100000000000000000n, | ||
"gatewayFeeRecipient": "0x70997970c51812dc3a010c7d01b50e0d17dc79c8", | ||
"maxFeePerGas": 2000000000n, | ||
"maxPriorityFeePerGas": 2000000000n, | ||
"nonce": 785, | ||
"to": "0x90f79bf6eb2c4f870365e785982e1f101e93b906", | ||
"type": "cip42", | ||
"value": 1000000000000000000n, | ||
} | ||
`) | ||
}) | ||
|
||
test('should parse a CIP42 transaction with access list', () => { | ||
const transactionWithAccessList: TransactionSerializableCIP42 = { | ||
feeCurrency: '0x765de816845861e75a25fca122bb6898b8b1282a', | ||
...transaction, | ||
chainId: 42270, | ||
accessList: [ | ||
{ | ||
address: '0x0000000000000000000000000000000000000000', | ||
storageKeys: [ | ||
'0x0000000000000000000000000000000000000000000000000000000000000001', | ||
'0x60fdd29ff912ce880cd3edaf9f932dc61d3dae823ea77e0323f94adb9f6a72fe', | ||
], | ||
}, | ||
], | ||
} | ||
|
||
const serialized = serializeTransactionCelo(transactionWithAccessList) | ||
|
||
expect(parseTransactionCelo(serialized)).toMatchInlineSnapshot(` | ||
{ | ||
"accessList": [ | ||
{ | ||
"address": "0x0000000000000000000000000000000000000000", | ||
"storageKeys": [ | ||
"0x0000000000000000000000000000000000000000000000000000000000000001", | ||
"0x60fdd29ff912ce880cd3edaf9f932dc61d3dae823ea77e0323f94adb9f6a72fe", | ||
], | ||
}, | ||
], | ||
"chainId": 42270, | ||
"feeCurrency": "0x765de816845861e75a25fca122bb6898b8b1282a", | ||
"gas": 21001n, | ||
"maxFeePerGas": 2000000000n, | ||
"maxPriorityFeePerGas": 2000000000n, | ||
"nonce": 785, | ||
"to": "0x90f79bf6eb2c4f870365e785982e1f101e93b906", | ||
"type": "cip42", | ||
"value": 1000000000000000000n, | ||
} | ||
`) | ||
}) | ||
|
||
test('should parse a CIP42 transaction with data as 0x', () => { | ||
const transactionWithData: TransactionSerializableCIP42 = { | ||
feeCurrency: '0x765de816845861e75a25fca122bb6898b8b1282a', | ||
...transaction, | ||
chainId: 42270, | ||
data: '0x', | ||
} | ||
|
||
const serialized = serializeTransactionCelo(transactionWithData) | ||
|
||
expect(parseTransactionCelo(serialized)).toMatchInlineSnapshot(` | ||
{ | ||
"chainId": 42270, | ||
"feeCurrency": "0x765de816845861e75a25fca122bb6898b8b1282a", | ||
"gas": 21001n, | ||
"maxFeePerGas": 2000000000n, | ||
"maxPriorityFeePerGas": 2000000000n, | ||
"nonce": 785, | ||
"to": "0x90f79bf6eb2c4f870365e785982e1f101e93b906", | ||
"type": "cip42", | ||
"value": 1000000000000000000n, | ||
} | ||
`) | ||
}) | ||
test('should parse a CIP42 transaction with data', () => { | ||
const transactionWithData: TransactionSerializableCIP42 = { | ||
...transaction, | ||
feeCurrency: '0x765de816845861e75a25fca122bb6898b8b1282a', | ||
chainId: 42270, | ||
data: '0x1234', | ||
} | ||
|
||
const serialized = serializeTransactionCelo(transactionWithData) | ||
|
||
expect(parseTransactionCelo(serialized)).toMatchInlineSnapshot(` | ||
{ | ||
"chainId": 42270, | ||
"data": "0x1234", | ||
"feeCurrency": "0x765de816845861e75a25fca122bb6898b8b1282a", | ||
"gas": 21001n, | ||
"maxFeePerGas": 2000000000n, | ||
"maxPriorityFeePerGas": 2000000000n, | ||
"nonce": 785, | ||
"to": "0x90f79bf6eb2c4f870365e785982e1f101e93b906", | ||
"type": "cip42", | ||
"value": 1000000000000000000n, | ||
} | ||
`) | ||
}) | ||
|
||
test('invalid transaction (all missing)', () => { | ||
expect(() => | ||
parseTransactionCelo(`0x7c${toRlp([]).slice(2)}`), | ||
).toThrowErrorMatchingInlineSnapshot(` | ||
"Invalid serialized transaction of type \\"cip42\\" was provided. | ||
Serialized Transaction: \\"0x7cc0\\" | ||
Missing Attributes: chainId, nonce, maxPriorityFeePerGas, maxFeePerGas, gas, feeCurrency, to, gatewayFeeRecipient, gatewayFee, value, data, accessList | ||
Version: viem@1.0.2" | ||
`) | ||
}) | ||
|
||
test('invalid transaction (some missing)', () => { | ||
expect(() => | ||
parseTransactionCelo(`0x7c${toRlp(['0x0', '0x1']).slice(2)}`), | ||
).toThrowErrorMatchingInlineSnapshot(` | ||
"Invalid serialized transaction of type \\"cip42\\" was provided. | ||
Serialized Transaction: \\"0x7cc20001\\" | ||
Missing Attributes: maxPriorityFeePerGas, maxFeePerGas, gas, feeCurrency, to, gatewayFeeRecipient, gatewayFee, value, data, accessList | ||
Version: viem@1.0.2" | ||
`) | ||
}) | ||
|
||
test('invalid transaction (missing signature)', () => { | ||
expect(() => | ||
parseTransactionCelo( | ||
`0x7c${toRlp([ | ||
'0x', | ||
'0x', | ||
'0x', | ||
'0x', | ||
'0x', | ||
'0x', | ||
'0x', | ||
'0x', | ||
'0x', | ||
'0x', | ||
'0x', | ||
'0x', | ||
'0x', | ||
]).slice(2)}`, | ||
), | ||
).toThrowErrorMatchingInlineSnapshot(` | ||
"Invalid serialized transaction of type \\"cip42\\" was provided. | ||
Serialized Transaction: \\"0x7ccd80808080808080808080808080\\" | ||
Missing Attributes: r, s | ||
Version: viem@1.0.2" | ||
`) | ||
}) | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,117 @@ | ||
import { InvalidSerializedTransactionError } from '../../errors/transaction.js' | ||
import { | ||
type Hex, | ||
type TransactionSerialized, | ||
hexToBigInt, | ||
hexToNumber, | ||
isHex, | ||
parseTransaction, | ||
sliceHex, | ||
} from '../../index.js' | ||
import { | ||
parseAccessList, | ||
toTransactionArray, | ||
} from '../../utils/transaction/parseTransaction.js' | ||
import { | ||
type CeloTransactionSerializable, | ||
type SerializedCIP42TransactionReturnType, | ||
type TransactionSerializableCIP42, | ||
assertTransactionCIP42, | ||
} from './serializers.js' | ||
|
||
import type { RecursiveArray } from '../../utils/encoding/toRlp.js' | ||
|
||
export function parseTransactionCelo( | ||
serializedTransaction: | ||
| TransactionSerialized | ||
| SerializedCIP42TransactionReturnType, | ||
): CeloTransactionSerializable { | ||
const serializedType = sliceHex(serializedTransaction, 0, 1) | ||
|
||
if (serializedType === '0x7c') { | ||
return parseTransactionCIP42( | ||
serializedTransaction as SerializedCIP42TransactionReturnType, | ||
) | ||
} | ||
|
||
return parseTransaction(serializedTransaction) | ||
} | ||
|
||
function parseTransactionCIP42( | ||
serializedTransaction: SerializedCIP42TransactionReturnType, | ||
): TransactionSerializableCIP42 { | ||
const transactionArray = toTransactionArray(serializedTransaction) | ||
|
||
const [ | ||
chainId, | ||
nonce, | ||
maxPriorityFeePerGas, | ||
maxFeePerGas, | ||
gas, | ||
feeCurrency, | ||
gatewayFeeRecipient, | ||
gatewayFee, | ||
to, | ||
value, | ||
data, | ||
accessList, | ||
v, | ||
r, | ||
s, | ||
] = transactionArray | ||
|
||
if (transactionArray.length !== 15 && transactionArray.length !== 12) { | ||
throw new InvalidSerializedTransactionError({ | ||
attributes: { | ||
chainId, | ||
nonce, | ||
maxPriorityFeePerGas, | ||
maxFeePerGas, | ||
gas, | ||
feeCurrency, | ||
to, | ||
gatewayFeeRecipient, | ||
gatewayFee, | ||
value, | ||
data, | ||
accessList, | ||
...(transactionArray.length > 12 | ||
? { | ||
v, | ||
r, | ||
s, | ||
} | ||
: {}), | ||
}, | ||
serializedTransaction, | ||
type: 'cip42', | ||
}) | ||
} | ||
|
||
const transaction: Partial<TransactionSerializableCIP42> = { | ||
chainId: hexToNumber(chainId as Hex), | ||
type: 'cip42', | ||
} | ||
|
||
if (isHex(to) && to !== '0x') transaction.to = to | ||
if (isHex(gas) && gas !== '0x') transaction.gas = hexToBigInt(gas) | ||
if (isHex(data) && data !== '0x') transaction.data = data | ||
if (isHex(nonce) && nonce !== '0x') transaction.nonce = hexToNumber(nonce) | ||
if (isHex(value) && value !== '0x') transaction.value = hexToBigInt(value) | ||
if (isHex(feeCurrency) && feeCurrency !== '0x') | ||
transaction.feeCurrency = feeCurrency | ||
if (isHex(gatewayFeeRecipient) && gatewayFeeRecipient !== '0x') | ||
transaction.gatewayFeeRecipient = gatewayFeeRecipient | ||
if (isHex(gatewayFee) && gatewayFee !== '0x') | ||
transaction.gatewayFee = hexToBigInt(gatewayFee) | ||
if (isHex(maxFeePerGas) && maxFeePerGas !== '0x') | ||
transaction.maxFeePerGas = hexToBigInt(maxFeePerGas) | ||
if (isHex(maxPriorityFeePerGas) && maxPriorityFeePerGas !== '0x') | ||
transaction.maxPriorityFeePerGas = hexToBigInt(maxPriorityFeePerGas) | ||
if (accessList.length !== 0 && accessList !== '0x') | ||
transaction.accessList = parseAccessList(accessList as RecursiveArray<Hex>) | ||
|
||
assertTransactionCIP42(transaction as TransactionSerializableCIP42) | ||
|
||
return transaction as TransactionSerializableCIP42 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.