ox 1.8.10 → 1.8.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (88) hide show
  1. package/CHANGELOG.md +30 -0
  2. package/dist/core/Authorization.d.ts.map +1 -1
  3. package/dist/core/Authorization.js +44 -6
  4. package/dist/core/Authorization.js.map +1 -1
  5. package/dist/core/Siwe.d.ts.map +1 -1
  6. package/dist/core/Siwe.js +1 -1
  7. package/dist/core/Siwe.js.map +1 -1
  8. package/dist/core/TxEnvelope.d.ts.map +1 -1
  9. package/dist/core/TxEnvelope.js +5 -3
  10. package/dist/core/TxEnvelope.js.map +1 -1
  11. package/dist/core/TxEnvelopeEip1559.d.ts.map +1 -1
  12. package/dist/core/TxEnvelopeEip1559.js +6 -2
  13. package/dist/core/TxEnvelopeEip1559.js.map +1 -1
  14. package/dist/core/TxEnvelopeEip4844.d.ts.map +1 -1
  15. package/dist/core/TxEnvelopeEip4844.js +4 -0
  16. package/dist/core/TxEnvelopeEip4844.js.map +1 -1
  17. package/dist/core/TxEnvelopeEip7702.d.ts.map +1 -1
  18. package/dist/core/TxEnvelopeEip7702.js +4 -0
  19. package/dist/core/TxEnvelopeEip7702.js.map +1 -1
  20. package/dist/core/TypedData.d.ts +8 -1
  21. package/dist/core/TypedData.d.ts.map +1 -1
  22. package/dist/core/TypedData.js +51 -14
  23. package/dist/core/TypedData.js.map +1 -1
  24. package/dist/core/internal/abiItem.d.ts.map +1 -1
  25. package/dist/core/internal/abiItem.js +44 -13
  26. package/dist/core/internal/abiItem.js.map +1 -1
  27. package/dist/core/internal/abiParameters.d.ts +2 -2
  28. package/dist/core/internal/abiParameters.d.ts.map +1 -1
  29. package/dist/core/internal/abiParameters.js +24 -7
  30. package/dist/core/internal/abiParameters.js.map +1 -1
  31. package/dist/core/internal/rpcSchemas/eth.d.ts +3 -2
  32. package/dist/core/internal/rpcSchemas/eth.d.ts.map +1 -1
  33. package/dist/tempo/AccountConfig.d.ts +7 -17
  34. package/dist/tempo/AccountConfig.d.ts.map +1 -1
  35. package/dist/tempo/AccountConfig.js +8 -26
  36. package/dist/tempo/AccountConfig.js.map +1 -1
  37. package/dist/tempo/AccountOperation.d.ts +1 -1
  38. package/dist/tempo/AccountOperation.js +1 -1
  39. package/dist/tempo/KeyAuthorization.d.ts +21 -6
  40. package/dist/tempo/KeyAuthorization.d.ts.map +1 -1
  41. package/dist/tempo/KeyAuthorization.js +7 -4
  42. package/dist/tempo/KeyAuthorization.js.map +1 -1
  43. package/dist/tempo/TxEnvelopeTempo.d.ts.map +1 -1
  44. package/dist/tempo/TxEnvelopeTempo.js +6 -2
  45. package/dist/tempo/TxEnvelopeTempo.js.map +1 -1
  46. package/dist/tempo/index.d.ts +1 -3
  47. package/dist/tempo/index.d.ts.map +1 -1
  48. package/dist/tempo/index.js +1 -3
  49. package/dist/tempo/index.js.map +1 -1
  50. package/dist/zod/Authorization.d.ts.map +1 -1
  51. package/dist/zod/Authorization.js +6 -9
  52. package/dist/zod/Authorization.js.map +1 -1
  53. package/package.json +5 -2
  54. package/src/core/Authorization.ts +43 -6
  55. package/src/core/Siwe.ts +1 -1
  56. package/src/core/TxEnvelope.ts +5 -3
  57. package/src/core/TxEnvelopeEip1559.ts +8 -2
  58. package/src/core/TxEnvelopeEip4844.ts +6 -0
  59. package/src/core/TxEnvelopeEip7702.ts +6 -0
  60. package/src/core/TypedData.ts +57 -13
  61. package/src/core/_test/AbiItem.test.ts +238 -4
  62. package/src/core/_test/AbiParameters.decode.test.ts +140 -1
  63. package/src/core/_test/Authorization.test.ts +166 -0
  64. package/src/core/_test/RpcSchema.test-d.ts +23 -1
  65. package/src/core/_test/Siwe.test.ts +53 -0
  66. package/src/core/_test/TransactionEnvelopeEip1559.test.ts +24 -0
  67. package/src/core/_test/TransactionEnvelopeEip4844.test.ts +13 -0
  68. package/src/core/_test/TransactionEnvelopeEip7702.test.ts +79 -0
  69. package/src/core/_test/TypedData.test.ts +188 -1
  70. package/src/core/internal/abiItem.ts +48 -17
  71. package/src/core/internal/abiParameters.ts +26 -8
  72. package/src/core/internal/rpcSchemas/eth.ts +2 -1
  73. package/src/tempo/AccountConfig.test.ts +34 -47
  74. package/src/tempo/AccountConfig.ts +9 -37
  75. package/src/tempo/AccountOperation.test.ts +36 -37
  76. package/src/tempo/AccountOperation.ts +1 -1
  77. package/src/tempo/KeyAuthorization.test.ts +6 -4
  78. package/src/tempo/KeyAuthorization.ts +26 -8
  79. package/src/tempo/SignatureEnvelope.test.ts +1 -3
  80. package/src/tempo/TxEnvelopeTempo.test.ts +26 -0
  81. package/src/tempo/TxEnvelopeTempo.ts +8 -2
  82. package/src/tempo/configurableAccounts.e2e.test.ts +1 -2
  83. package/src/tempo/e2e.test.ts +73 -17
  84. package/src/tempo/index.ts +1 -3
  85. package/src/version.ts +1 -1
  86. package/src/zod/Authorization.ts +6 -24
  87. package/src/zod/_test/Authorization.test.ts +35 -2
  88. package/src/zod/_test/TransactionRequest.test.ts +1 -1
@@ -1021,7 +1021,201 @@ describe('fromAbi', () => {
1021
1021
  `)
1022
1022
  })
1023
1023
 
1024
+ test('behavior: overloads: bytes32 vs bytes', () => {
1025
+ expect(() =>
1026
+ AbiItem.fromAbi(
1027
+ Abi.from([
1028
+ 'function isValidSignature(bytes32 hash, bytes signature)',
1029
+ 'function isValidSignature(bytes data, bytes signature)',
1030
+ ]),
1031
+ 'isValidSignature',
1032
+ {
1033
+ args: [
1034
+ '0x0000000000000000000000000000000000000000000000000000000000000001',
1035
+ '0x1234',
1036
+ ],
1037
+ },
1038
+ ),
1039
+ ).toThrowErrorMatchingInlineSnapshot(`
1040
+ [AbiItem.AmbiguityError: Found ambiguous types in overloaded ABI Items.
1041
+
1042
+ \`bytes\` in \`isValidSignature(bytes,bytes)\`, and
1043
+ \`bytes32\` in \`isValidSignature(bytes32,bytes)\`
1044
+
1045
+ These types encode differently and cannot be distinguished at runtime.
1046
+ Remove one of the ambiguous items in the ABI.]
1047
+ `)
1048
+ })
1049
+
1050
+ test('behavior: overloads: uint256 vs int256', () => {
1051
+ expect(() =>
1052
+ AbiItem.fromAbi(
1053
+ Abi.from(['function foo(uint256 x)', 'function foo(int256 x)']),
1054
+ 'foo',
1055
+ { args: [1n] },
1056
+ ),
1057
+ ).toThrowErrorMatchingInlineSnapshot(`
1058
+ [AbiItem.AmbiguityError: Found ambiguous types in overloaded ABI Items.
1059
+
1060
+ \`int256\` in \`foo(int256)\`, and
1061
+ \`uint256\` in \`foo(uint256)\`
1062
+
1063
+ These types encode differently and cannot be distinguished at runtime.
1064
+ Remove one of the ambiguous items in the ABI.]
1065
+ `)
1066
+
1067
+ expect(
1068
+ AbiItem.fromAbi(
1069
+ Abi.from(['function foo(uint256 x)', 'function foo(int256 x)']),
1070
+ 'foo',
1071
+ { args: [-1n] },
1072
+ ).inputs,
1073
+ ).toMatchInlineSnapshot(`
1074
+ [
1075
+ {
1076
+ "name": "x",
1077
+ "type": "int256",
1078
+ },
1079
+ ]
1080
+ `)
1081
+ })
1082
+
1083
+ test('behavior: overloads: uint256[] vs uint256[2]', () => {
1084
+ expect(() =>
1085
+ AbiItem.fromAbi(
1086
+ Abi.from(['function foo(uint256[] x)', 'function foo(uint256[2] x)']),
1087
+ 'foo',
1088
+ { args: [[1n, 1n]] },
1089
+ ),
1090
+ ).toThrowErrorMatchingInlineSnapshot(`
1091
+ [AbiItem.AmbiguityError: Found ambiguous types in overloaded ABI Items.
1092
+
1093
+ \`uint256[2]\` in \`foo(uint256[2])\`, and
1094
+ \`uint256[]\` in \`foo(uint256[])\`
1095
+
1096
+ These types encode differently and cannot be distinguished at runtime.
1097
+ Remove one of the ambiguous items in the ABI.]
1098
+ `)
1099
+
1100
+ expect(
1101
+ AbiItem.fromAbi(
1102
+ Abi.from(['function foo(uint256[] x)', 'function foo(uint256[2] x)']),
1103
+ 'foo',
1104
+ { args: [[1n, 1n, 1n]] },
1105
+ ).inputs,
1106
+ ).toMatchInlineSnapshot(`
1107
+ [
1108
+ {
1109
+ "name": "x",
1110
+ "type": "uint256[]",
1111
+ },
1112
+ ]
1113
+ `)
1114
+ })
1115
+
1116
+ test('behavior: overloads: uint256[2][] vs uint256[][2]', () => {
1117
+ expect(() =>
1118
+ AbiItem.fromAbi(
1119
+ Abi.from([
1120
+ 'function foo(uint256[2][] x)',
1121
+ 'function foo(uint256[][2] x)',
1122
+ ]),
1123
+ 'foo',
1124
+ {
1125
+ args: [
1126
+ [
1127
+ [1n, 1n],
1128
+ [1n, 1n],
1129
+ ],
1130
+ ],
1131
+ },
1132
+ ),
1133
+ ).toThrowErrorMatchingInlineSnapshot(`
1134
+ [AbiItem.AmbiguityError: Found ambiguous types in overloaded ABI Items.
1135
+
1136
+ \`uint256[]\` in \`foo(uint256[][2])\`, and
1137
+ \`uint256[2]\` in \`foo(uint256[2][])\`
1138
+
1139
+ These types encode differently and cannot be distinguished at runtime.
1140
+ Remove one of the ambiguous items in the ABI.]
1141
+ `)
1142
+ })
1143
+
1144
+ test('behavior: overloads: selector chooses one of two matches', () => {
1145
+ expect(
1146
+ AbiItem.fromAbi(
1147
+ Abi.from([
1148
+ 'function isValidSignature(bytes32 hash, bytes signature)',
1149
+ 'function isValidSignature(bytes data, bytes signature)',
1150
+ ]),
1151
+ '0x1626ba7e',
1152
+ {
1153
+ args: [
1154
+ '0x0000000000000000000000000000000000000000000000000000000000000001',
1155
+ '0x1234',
1156
+ ],
1157
+ },
1158
+ ).inputs,
1159
+ ).toMatchInlineSnapshot(`
1160
+ [
1161
+ {
1162
+ "name": "hash",
1163
+ "type": "bytes32",
1164
+ },
1165
+ {
1166
+ "name": "signature",
1167
+ "type": "bytes",
1168
+ },
1169
+ ]
1170
+ `)
1171
+ })
1172
+
1024
1173
  describe('getAmbiguousTypes', () => {
1174
+ test('bytes', () => {
1175
+ expect(
1176
+ getAmbiguousTypes(
1177
+ AbiParameters.from('bytes32'),
1178
+ AbiParameters.from('bytes'),
1179
+ [
1180
+ '0x0000000000000000000000000000000000000000000000000000000000000001',
1181
+ ],
1182
+ ),
1183
+ ).toMatchInlineSnapshot(`
1184
+ [
1185
+ "bytes32",
1186
+ "bytes",
1187
+ ]
1188
+ `)
1189
+ })
1190
+
1191
+ test('integers', () => {
1192
+ expect(
1193
+ getAmbiguousTypes(
1194
+ AbiParameters.from('uint256'),
1195
+ AbiParameters.from('int256'),
1196
+ [1n],
1197
+ ),
1198
+ ).toMatchInlineSnapshot(`
1199
+ [
1200
+ "uint256",
1201
+ "int256",
1202
+ ]
1203
+ `)
1204
+
1205
+ expect(
1206
+ getAmbiguousTypes(
1207
+ AbiParameters.from('uint256'),
1208
+ AbiParameters.from('uint128'),
1209
+ [69420n],
1210
+ ),
1211
+ ).toMatchInlineSnapshot(`
1212
+ [
1213
+ "uint256",
1214
+ "uint128",
1215
+ ]
1216
+ `)
1217
+ })
1218
+
1025
1219
  test('string/address', () => {
1026
1220
  expect(
1027
1221
  getAmbiguousTypes(
@@ -1163,7 +1357,7 @@ describe('fromAbi', () => {
1163
1357
  expect(
1164
1358
  getAmbiguousTypes(
1165
1359
  AbiParameters.from('uint256, (address)'),
1166
- AbiParameters.from('uint128, (bytes20)'),
1360
+ AbiParameters.from('uint256, (bytes20)'),
1167
1361
  [69420n, ['0xA0Cf798816D4b9b9866b5330EEa46a18382f251e']],
1168
1362
  ),
1169
1363
  ).toMatchInlineSnapshot(`
@@ -1175,7 +1369,7 @@ describe('fromAbi', () => {
1175
1369
  expect(
1176
1370
  getAmbiguousTypes(
1177
1371
  AbiParameters.from('uint256, (string, (address))'),
1178
- AbiParameters.from('uint128, (string, (bytes20))'),
1372
+ AbiParameters.from('uint256, (string, (bytes20))'),
1179
1373
  [69420n, ['foo', ['0xA0Cf798816D4b9b9866b5330EEa46a18382f251e']]],
1180
1374
  ),
1181
1375
  ).toMatchInlineSnapshot(`
@@ -1187,7 +1381,7 @@ describe('fromAbi', () => {
1187
1381
  expect(
1188
1382
  getAmbiguousTypes(
1189
1383
  AbiParameters.from('uint256, (string, (address, bytes))'),
1190
- AbiParameters.from('uint128, (string, (bytes20, address))'),
1384
+ AbiParameters.from('uint256, (string, (bytes20, address))'),
1191
1385
  [
1192
1386
  123n,
1193
1387
  [
@@ -1360,7 +1554,19 @@ describe('fromAbi', () => {
1360
1554
  { arg: 'true', abiParameter: { type: 'bool' }, expected: false },
1361
1555
  // bytes
1362
1556
  { arg: 'foo', abiParameter: { type: 'bytes' }, expected: true },
1363
- { arg: 'foo', abiParameter: { type: 'bytes32' }, expected: true },
1557
+ { arg: 'foo', abiParameter: { type: 'bytes32' }, expected: false },
1558
+ { arg: '0x1234', abiParameter: { type: 'bytes2' }, expected: true },
1559
+ { arg: '0x1234', abiParameter: { type: 'bytes4' }, expected: false },
1560
+ {
1561
+ arg: Bytes.fromString('foo'),
1562
+ abiParameter: { type: 'bytes3' },
1563
+ expected: true,
1564
+ },
1565
+ {
1566
+ arg: Bytes.fromString('foo'),
1567
+ abiParameter: { type: 'bytes4' },
1568
+ expected: false,
1569
+ },
1364
1570
  {
1365
1571
  arg: Bytes.fromString('foo'),
1366
1572
  abiParameter: { type: 'bytes' },
@@ -1381,6 +1587,34 @@ describe('fromAbi', () => {
1381
1587
  { arg: 1, abiParameter: { type: 'uint256' }, expected: true },
1382
1588
  { arg: 1, abiParameter: { type: 'int69' }, expected: false },
1383
1589
  { arg: 1, abiParameter: { type: 'uint69' }, expected: false },
1590
+ { arg: -129, abiParameter: { type: 'int8' }, expected: false },
1591
+ { arg: -128, abiParameter: { type: 'int8' }, expected: true },
1592
+ { arg: 127, abiParameter: { type: 'int8' }, expected: true },
1593
+ { arg: 128, abiParameter: { type: 'int8' }, expected: false },
1594
+ { arg: -1n, abiParameter: { type: 'uint8' }, expected: false },
1595
+ { arg: 0n, abiParameter: { type: 'uint8' }, expected: true },
1596
+ { arg: 255n, abiParameter: { type: 'uint8' }, expected: true },
1597
+ { arg: 256n, abiParameter: { type: 'uint8' }, expected: false },
1598
+ { arg: 2n ** 256n - 1n, abiParameter: { type: 'uint256' }, expected: true },
1599
+ { arg: 2n ** 256n, abiParameter: { type: 'uint256' }, expected: false },
1600
+ { arg: -(2n ** 255n), abiParameter: { type: 'int256' }, expected: true },
1601
+ {
1602
+ arg: -(2n ** 255n) - 1n,
1603
+ abiParameter: { type: 'int256' },
1604
+ expected: false,
1605
+ },
1606
+ { arg: Number.NaN, abiParameter: { type: 'uint256' }, expected: false },
1607
+ {
1608
+ arg: Number.POSITIVE_INFINITY,
1609
+ abiParameter: { type: 'uint256' },
1610
+ expected: false,
1611
+ },
1612
+ { arg: 1.5, abiParameter: { type: 'uint256' }, expected: false },
1613
+ {
1614
+ arg: Number.MAX_SAFE_INTEGER + 1,
1615
+ abiParameter: { type: 'uint256' },
1616
+ expected: true,
1617
+ },
1384
1618
  // string
1385
1619
  { arg: 'foo', abiParameter: { type: 'string' }, expected: true },
1386
1620
  { arg: 1, abiParameter: { type: 'string' }, expected: false },
@@ -1,4 +1,4 @@
1
- import { AbiItem, AbiParameters, type Address, Bytes, type Hex } from 'ox'
1
+ import { AbiItem, AbiParameters, Address, Bytes, Hex } from 'ox'
2
2
  import { assertType, describe, expect, test } from 'vp/test'
3
3
 
4
4
  import {
@@ -1887,3 +1887,142 @@ describe('zero-width types', () => {
1887
1887
  }
1888
1888
  })
1889
1889
  })
1890
+
1891
+ describe('integer range', () => {
1892
+ test('uint8: max', () => {
1893
+ expect(
1894
+ AbiParameters.decode(
1895
+ AbiParameters.from('uint8'),
1896
+ '0x00000000000000000000000000000000000000000000000000000000000000ff',
1897
+ ),
1898
+ ).toEqual([255])
1899
+ })
1900
+
1901
+ test('uint8: above max', () => {
1902
+ expect(() =>
1903
+ AbiParameters.decode(
1904
+ AbiParameters.from('uint8'),
1905
+ '0x0000000000000000000000000000000000000000000000000000000000000100',
1906
+ ),
1907
+ ).toThrowErrorMatchingInlineSnapshot(
1908
+ `[Hex.IntegerOutOfRangeError: Number \`256\` is not in safe 8-bit unsigned integer range (\`0\` to \`255\`)]`,
1909
+ )
1910
+ })
1911
+
1912
+ test('int8: -1', () => {
1913
+ expect(
1914
+ AbiParameters.decode(
1915
+ AbiParameters.from('int8'),
1916
+ '0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff',
1917
+ ),
1918
+ ).toEqual([-1])
1919
+ })
1920
+
1921
+ test('int8: missing sign extension', () => {
1922
+ expect(() =>
1923
+ AbiParameters.decode(
1924
+ AbiParameters.from('int8'),
1925
+ '0x0000000000000000000000000000000000000000000000000000000000000080',
1926
+ ),
1927
+ ).toThrowErrorMatchingInlineSnapshot(
1928
+ `[Hex.IntegerOutOfRangeError: Number \`128\` is not in safe 8-bit signed integer range (\`-128\` to \`127\`)]`,
1929
+ )
1930
+ })
1931
+
1932
+ test('int8: below min', () => {
1933
+ expect(() =>
1934
+ AbiParameters.decode(
1935
+ AbiParameters.from('int8'),
1936
+ '0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f',
1937
+ ),
1938
+ ).toThrowErrorMatchingInlineSnapshot(
1939
+ `[Hex.IntegerOutOfRangeError: Number \`-129\` is not in safe 8-bit signed integer range (\`-128\` to \`127\`)]`,
1940
+ )
1941
+ })
1942
+
1943
+ test('uint8[]: element above max', () => {
1944
+ expect(() =>
1945
+ AbiParameters.decode(
1946
+ AbiParameters.from('uint8[]'),
1947
+ '0x000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000100',
1948
+ ),
1949
+ ).toThrowErrorMatchingInlineSnapshot(
1950
+ `[Hex.IntegerOutOfRangeError: Number \`256\` is not in safe 8-bit unsigned integer range (\`0\` to \`255\`)]`,
1951
+ )
1952
+ })
1953
+
1954
+ test('uint16: 2^64', () => {
1955
+ expect(() =>
1956
+ AbiParameters.decode(
1957
+ AbiParameters.from('uint16'),
1958
+ '0x0000000000000000000000000000000000000000000000010000000000000000',
1959
+ ),
1960
+ ).toThrowErrorMatchingInlineSnapshot(
1961
+ `[Hex.IntegerOutOfRangeError: Number \`18446744073709551616\` is not in safe 16-bit unsigned integer range (\`0\` to \`65535\`)]`,
1962
+ )
1963
+ })
1964
+
1965
+ test('every size: min and max round-trip, out of range throws', () => {
1966
+ for (let size = 8; size <= 256; size += 8) {
1967
+ for (const signed of [false, true]) {
1968
+ const type = (signed ? 'int' : 'uint') + size
1969
+ const parameters = AbiParameters.from(type)
1970
+ const max = (1n << BigInt(signed ? size - 1 : size)) - 1n
1971
+ const min = signed ? -max - 1n : 0n
1972
+ const cast = (value: bigint) => (size > 48 ? value : Number(value))
1973
+
1974
+ for (const value of [min, max])
1975
+ expect(
1976
+ AbiParameters.decode(
1977
+ parameters,
1978
+ AbiParameters.encode(parameters, [cast(value)] as never),
1979
+ ),
1980
+ ).toEqual([cast(value)])
1981
+
1982
+ if (size === 256) continue
1983
+ const word = (value: bigint) =>
1984
+ Hex.fromNumber(BigInt.asUintN(256, value), { size: 32 })
1985
+ expect(() => AbiParameters.decode(parameters, word(max + 1n))).toThrow(
1986
+ Hex.IntegerOutOfRangeError,
1987
+ )
1988
+ if (signed)
1989
+ expect(() =>
1990
+ AbiParameters.decode(parameters, word(min - 1n)),
1991
+ ).toThrow(Hex.IntegerOutOfRangeError)
1992
+ }
1993
+ }
1994
+ })
1995
+ })
1996
+
1997
+ describe('address padding', () => {
1998
+ test('dirty high bytes', () => {
1999
+ expect(() =>
2000
+ AbiParameters.decode(
2001
+ AbiParameters.from('address'),
2002
+ '0x00000000000000000000000114dc79964da2c08b23698b3d3cc7ca32193d9955',
2003
+ ),
2004
+ ).toThrowErrorMatchingInlineSnapshot(`
2005
+ [Address.InvalidAddressError: Address "0x00000000000000000000000114dc79964da2c08b23698b3d3cc7ca32193d9955" is invalid.
2006
+
2007
+ Details: Address is not a 20 byte (40 hexadecimal character) value.]
2008
+ `)
2009
+ })
2010
+
2011
+ test('dirty first byte', () => {
2012
+ expect(() =>
2013
+ AbiParameters.decode(
2014
+ AbiParameters.from('address'),
2015
+ '0xff000000000000000000000014dc79964da2c08b23698b3d3cc7ca32193d9955',
2016
+ ),
2017
+ ).toThrow(Address.InvalidAddressError)
2018
+ })
2019
+
2020
+ test('address[2]: second element dirty', () => {
2021
+ expect(() =>
2022
+ AbiParameters.decode(
2023
+ AbiParameters.from('address[2]'),
2024
+ '0x00000000000000000000000014dc79964da2c08b23698b3d3cc7ca32193d99550000000000000000000000ff14dc79964da2c08b23698b3d3cc7ca32193d9955',
2025
+ ),
2026
+ ).toThrow(Address.InvalidAddressError)
2027
+ })
2028
+ })
@@ -151,6 +151,43 @@ describe('fromRpc', () => {
151
151
  }
152
152
  `)
153
153
  })
154
+
155
+ test('behavior: yParity above 1', () => {
156
+ expect(
157
+ Authorization.fromRpc({
158
+ address: '0xc498cea4bfedbb8de856491bdd9967e677775f4f',
159
+ chainId: '0x0',
160
+ nonce: '0x6',
161
+ r: '0x44754b7f217808e21f1935cfa450380e17a3ab8f2c6996c0190d678f0c14a512',
162
+ s: '0x27a6b869798cbbbd8d538dbc95b9bb28783bf08f8ef88ad6bb82552d0b47b191',
163
+ yParity: '0x1c',
164
+ }),
165
+ ).toMatchInlineSnapshot(`
166
+ {
167
+ "address": "0xc498cea4bfedbb8de856491bdd9967e677775f4f",
168
+ "chainId": 0,
169
+ "nonce": 6n,
170
+ "r": "0x44754b7f217808e21f1935cfa450380e17a3ab8f2c6996c0190d678f0c14a512",
171
+ "s": "0x27a6b869798cbbbd8d538dbc95b9bb28783bf08f8ef88ad6bb82552d0b47b191",
172
+ "yParity": 28,
173
+ }
174
+ `)
175
+ })
176
+
177
+ test('error: yParity above 2**8 - 1', () => {
178
+ expect(() =>
179
+ Authorization.fromRpc({
180
+ address: '0xc498cea4bfedbb8de856491bdd9967e677775f4f',
181
+ chainId: '0x0',
182
+ nonce: '0x6',
183
+ r: '0x44754b7f217808e21f1935cfa450380e17a3ab8f2c6996c0190d678f0c14a512',
184
+ s: '0x27a6b869798cbbbd8d538dbc95b9bb28783bf08f8ef88ad6bb82552d0b47b191',
185
+ yParity: '0x100',
186
+ }),
187
+ ).toThrowErrorMatchingInlineSnapshot(
188
+ `[Hex.IntegerOutOfRangeError: Number \`256\` is not in safe 8-bit unsigned integer range (\`0\` to \`255\`)]`,
189
+ )
190
+ })
154
191
  })
155
192
 
156
193
  describe('fromRpcList', () => {
@@ -251,6 +288,44 @@ describe('fromTuple', () => {
251
288
  }
252
289
  `)
253
290
  })
291
+
292
+ test('behavior: yParity above 1', () => {
293
+ const tuple = [
294
+ '0x',
295
+ '0xc498cea4bfedbb8de856491bdd9967e677775f4f',
296
+ '0x06',
297
+ '0x1c',
298
+ '0x44754b7f217808e21f1935cfa450380e17a3ab8f2c6996c0190d678f0c14a512',
299
+ '0x27a6b869798cbbbd8d538dbc95b9bb28783bf08f8ef88ad6bb82552d0b47b191',
300
+ ] as const satisfies Authorization.Tuple
301
+ const authorization = Authorization.fromTuple(tuple)
302
+ expect(authorization).toMatchInlineSnapshot(`
303
+ {
304
+ "address": "0xc498cea4bfedbb8de856491bdd9967e677775f4f",
305
+ "chainId": 0,
306
+ "nonce": 6n,
307
+ "r": "0x44754b7f217808e21f1935cfa450380e17a3ab8f2c6996c0190d678f0c14a512",
308
+ "s": "0x27a6b869798cbbbd8d538dbc95b9bb28783bf08f8ef88ad6bb82552d0b47b191",
309
+ "yParity": 28,
310
+ }
311
+ `)
312
+ })
313
+
314
+ test('error: yParity above 2**8 - 1', () => {
315
+ const tuple = [
316
+ '0x',
317
+ '0xc498cea4bfedbb8de856491bdd9967e677775f4f',
318
+ '0x06',
319
+ '0x0100',
320
+ '0x44754b7f217808e21f1935cfa450380e17a3ab8f2c6996c0190d678f0c14a512',
321
+ '0x27a6b869798cbbbd8d538dbc95b9bb28783bf08f8ef88ad6bb82552d0b47b191',
322
+ ] as const satisfies Authorization.Tuple
323
+ expect(() =>
324
+ Authorization.fromTuple(tuple),
325
+ ).toThrowErrorMatchingInlineSnapshot(
326
+ `[Hex.IntegerOutOfRangeError: Number \`256\` is not in safe 8-bit unsigned integer range (\`0\` to \`255\`)]`,
327
+ )
328
+ })
254
329
  })
255
330
 
256
331
  describe('fromTupleList', () => {
@@ -454,6 +529,43 @@ describe('toRpc', () => {
454
529
  expect(fromBigint).toEqual(fromNumber)
455
530
  expect(fromBigint).toEqual(fromHex)
456
531
  })
532
+
533
+ test('behavior: yParity above 1', () => {
534
+ expect(
535
+ Authorization.toRpc({
536
+ address: '0xc498cea4bfedbb8de856491bdd9967e677775f4f',
537
+ chainId: 0,
538
+ nonce: 6n,
539
+ r: '0x44754b7f217808e21f1935cfa450380e17a3ab8f2c6996c0190d678f0c14a512',
540
+ s: '0x27a6b869798cbbbd8d538dbc95b9bb28783bf08f8ef88ad6bb82552d0b47b191',
541
+ yParity: 28,
542
+ }),
543
+ ).toMatchInlineSnapshot(`
544
+ {
545
+ "address": "0xc498cea4bfedbb8de856491bdd9967e677775f4f",
546
+ "chainId": "0x0",
547
+ "nonce": "0x6",
548
+ "r": "0x44754b7f217808e21f1935cfa450380e17a3ab8f2c6996c0190d678f0c14a512",
549
+ "s": "0x27a6b869798cbbbd8d538dbc95b9bb28783bf08f8ef88ad6bb82552d0b47b191",
550
+ "yParity": "0x1c",
551
+ }
552
+ `)
553
+ })
554
+
555
+ test('error: yParity above 2**8 - 1', () => {
556
+ expect(() =>
557
+ Authorization.toRpc({
558
+ address: '0xc498cea4bfedbb8de856491bdd9967e677775f4f',
559
+ chainId: 0,
560
+ nonce: 6n,
561
+ r: '0x44754b7f217808e21f1935cfa450380e17a3ab8f2c6996c0190d678f0c14a512',
562
+ s: '0x27a6b869798cbbbd8d538dbc95b9bb28783bf08f8ef88ad6bb82552d0b47b191',
563
+ yParity: 256,
564
+ }),
565
+ ).toThrowErrorMatchingInlineSnapshot(
566
+ `[Hex.IntegerOutOfRangeError: Number \`256\` is not in safe 8-bit unsigned integer range (\`0\` to \`255\`)]`,
567
+ )
568
+ })
457
569
  })
458
570
 
459
571
  describe('toRpcList', () => {
@@ -540,6 +652,60 @@ describe('toTuple', () => {
540
652
  `)
541
653
  }
542
654
  })
655
+
656
+ test('behavior: yParity above 1', () => {
657
+ const authorization = Authorization.from({
658
+ address: '0xc498cea4bfedbb8de856491bdd9967e677775f4f',
659
+ chainId: 0,
660
+ nonce: 6n,
661
+ r: '0x44754b7f217808e21f1935cfa450380e17a3ab8f2c6996c0190d678f0c14a512',
662
+ s: '0x27a6b869798cbbbd8d538dbc95b9bb28783bf08f8ef88ad6bb82552d0b47b191',
663
+ yParity: 28,
664
+ })
665
+ const tuple = Authorization.toTuple(authorization)
666
+ expect(tuple).toMatchInlineSnapshot(`
667
+ [
668
+ "0x",
669
+ "0xc498cea4bfedbb8de856491bdd9967e677775f4f",
670
+ "0x6",
671
+ "0x1c",
672
+ "0x44754b7f217808e21f1935cfa450380e17a3ab8f2c6996c0190d678f0c14a512",
673
+ "0x27a6b869798cbbbd8d538dbc95b9bb28783bf08f8ef88ad6bb82552d0b47b191",
674
+ ]
675
+ `)
676
+ })
677
+
678
+ test('error: yParity above 2**8 - 1', () => {
679
+ const authorization = Authorization.from({
680
+ address: '0xc498cea4bfedbb8de856491bdd9967e677775f4f',
681
+ chainId: 0,
682
+ nonce: 6n,
683
+ r: '0x44754b7f217808e21f1935cfa450380e17a3ab8f2c6996c0190d678f0c14a512',
684
+ s: '0x27a6b869798cbbbd8d538dbc95b9bb28783bf08f8ef88ad6bb82552d0b47b191',
685
+ yParity: 256,
686
+ })
687
+ expect(() =>
688
+ Authorization.toTuple(authorization),
689
+ ).toThrowErrorMatchingInlineSnapshot(
690
+ `[Hex.IntegerOutOfRangeError: Number \`256\` is not in safe 8-bit unsigned integer range (\`0\` to \`255\`)]`,
691
+ )
692
+ })
693
+
694
+ test('error: yParity not an integer', () => {
695
+ const authorization = Authorization.from({
696
+ address: '0xc498cea4bfedbb8de856491bdd9967e677775f4f',
697
+ chainId: 0,
698
+ nonce: 6n,
699
+ r: '0x44754b7f217808e21f1935cfa450380e17a3ab8f2c6996c0190d678f0c14a512',
700
+ s: '0x27a6b869798cbbbd8d538dbc95b9bb28783bf08f8ef88ad6bb82552d0b47b191',
701
+ yParity: Number.NaN,
702
+ })
703
+ expect(() =>
704
+ Authorization.toTuple(authorization),
705
+ ).toThrowErrorMatchingInlineSnapshot(
706
+ `[Hex.IntegerOutOfRangeError: Number \`NaN\` is not in safe 8-bit unsigned integer range (\`0\` to \`255\`)]`,
707
+ )
708
+ })
543
709
  })
544
710
 
545
711
  describe('toTupleList', () => {
@@ -1,4 +1,4 @@
1
- import { RpcSchema } from 'ox'
1
+ import { type Frame, RpcSchema } from 'ox'
2
2
  import { z } from 'ox/zod'
3
3
  import { describe, expectTypeOf, test } from 'vp/test'
4
4
 
@@ -246,6 +246,28 @@ test('frame fill responses do not require raw bytes or lookup metadata', () => {
246
246
  expectTypeOf(filled).toExtend<Filled>()
247
247
  })
248
248
 
249
+ test('fill responses expose fields not shared by every envelope as optional', () => {
250
+ type Tx = Extract<
251
+ RpcSchema.Eth,
252
+ { Request: { method: 'eth_fillTransaction' } }
253
+ >['ReturnType']['tx']
254
+ expectTypeOf<Tx['from']>().toEqualTypeOf<`0x${string}`>()
255
+ expectTypeOf<Tx['gas']>().toEqualTypeOf<`0x${string}` | undefined>()
256
+ expectTypeOf<Tx['frames']>().toEqualTypeOf<readonly Frame.Rpc[] | undefined>()
257
+
258
+ type Eip1559 = Extract<Tx, { type: '0x2' }>
259
+ expectTypeOf<Eip1559['gas']>().toEqualTypeOf<`0x${string}` | undefined>()
260
+ expectTypeOf<Eip1559['gasPrice']>().toEqualTypeOf<undefined>()
261
+ expectTypeOf<Eip1559['maxFeePerGas']>().toEqualTypeOf<
262
+ `0x${string}` | undefined
263
+ >()
264
+ expectTypeOf<Eip1559['frames']>().toEqualTypeOf<undefined>()
265
+
266
+ type Eip8141 = Extract<Tx, { type: '0x6' }>
267
+ expectTypeOf<Eip8141['frames']>().toEqualTypeOf<readonly Frame.Rpc[]>()
268
+ expectTypeOf<Eip8141['gas']>().toEqualTypeOf<undefined>()
269
+ })
270
+
249
271
  test('frame simulation results expose payer and frame results', () => {
250
272
  type Result = Extract<
251
273
  RpcSchema.Eth,