@rocketh/signer 0.10.5 → 0.10.6

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.
package/dist/index.cjs CHANGED
@@ -2758,7 +2758,7 @@ function checksum(len, fn) {
2758
2758
  // -----------
2759
2759
  const genBase58 = (abc) => chain(radix(58), alphabet(abc), join(''));
2760
2760
  const base58 = /* @__PURE__ */ genBase58('123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz');
2761
- const createBase58check = (sha256) => chain(checksum(4, (data) => sha256(sha256(data))), base58);
2761
+ const createBase58check = (sha256) => chain(checksum(4, (data) => sha256(sha256(data))), base58);
2762
2762
  // legacy export, bad name
2763
2763
  const base58check$1 = createBase58check;
2764
2764
 
@@ -3098,7 +3098,7 @@ function mnemonicToSeedSync(mnemonic, passphrase = '') {
3098
3098
  return pbkdf2(sha512, normalize(mnemonic).nfkd, salt(passphrase), { c: 2048, dkLen: 64 });
3099
3099
  }
3100
3100
 
3101
- const version = '2.9.16';
3101
+ const version = '2.16.2';
3102
3102
 
3103
3103
  const getVersion = () => `viem@${version}`;
3104
3104
 
@@ -3155,7 +3155,7 @@ class BaseError extends Error {
3155
3155
  ...(args.metaMessages ? [...args.metaMessages, ''] : []),
3156
3156
  ...(docsPath
3157
3157
  ? [
3158
- `Docs: https://viem.sh${docsPath}${args.docsSlug ? `#${args.docsSlug}` : ''}`,
3158
+ `Docs: ${args.docsBaseUrl ?? 'https://viem.sh'}${docsPath}${args.docsSlug ? `#${args.docsSlug}` : ''}`,
3159
3159
  ]
3160
3160
  : []),
3161
3161
  ...(details ? [`Details: ${details}`] : []),
@@ -3952,12 +3952,24 @@ function keccak256(value, to_) {
3952
3952
  return toHex(bytes);
3953
3953
  }
3954
3954
 
3955
- function checksumAddress(address_, chainId) {
3956
- const hexAddress = chainId
3957
- ? `${chainId}${address_.toLowerCase()}`
3958
- : address_.substring(2).toLowerCase();
3955
+ const checksumAddressCache = /*#__PURE__*/ new LruMap(8192);
3956
+ function checksumAddress(address_,
3957
+ /**
3958
+ * Warning: EIP-1191 checksum addresses are generally not backwards compatible with the
3959
+ * wider Ethereum ecosystem, meaning it will break when validated against an application/tool
3960
+ * that relies on EIP-55 checksum encoding (checksum without chainId).
3961
+ *
3962
+ * It is highly recommended to not use this feature unless you
3963
+ * know what you are doing.
3964
+ *
3965
+ * See more: https://github.com/ethereum/EIPs/issues/1121
3966
+ */
3967
+ chainId) {
3968
+ if (checksumAddressCache.has(`${address_}.${chainId}`))
3969
+ return checksumAddressCache.get(`${address_}.${chainId}`);
3970
+ const hexAddress = address_.substring(2).toLowerCase();
3959
3971
  const hash = keccak256(stringToBytes(hexAddress), 'bytes');
3960
- const address = (chainId ? hexAddress.substring(`${chainId}0x`.length) : hexAddress).split('');
3972
+ const address = (hexAddress).split('');
3961
3973
  for (let i = 0; i < 40; i += 2) {
3962
3974
  if (hash[i >> 1] >> 4 >= 8 && address[i]) {
3963
3975
  address[i] = address[i].toUpperCase();
@@ -3966,15 +3978,19 @@ function checksumAddress(address_, chainId) {
3966
3978
  address[i + 1] = address[i + 1].toUpperCase();
3967
3979
  }
3968
3980
  }
3969
- return `0x${address.join('')}`;
3981
+ const result = `0x${address.join('')}`;
3982
+ checksumAddressCache.set(`${address_}.${chainId}`, result);
3983
+ return result;
3970
3984
  }
3971
3985
 
3972
3986
  const addressRegex = /^0x[a-fA-F0-9]{40}$/;
3987
+ /** @internal */
3973
3988
  const isAddressCache = /*#__PURE__*/ new LruMap(8192);
3974
3989
  function isAddress(address, options) {
3975
3990
  const { strict = true } = options ?? {};
3976
- if (isAddressCache.has(address))
3977
- return isAddressCache.get(address);
3991
+ const cacheKey = `${address}.${strict}`;
3992
+ if (isAddressCache.has(cacheKey))
3993
+ return isAddressCache.get(cacheKey);
3978
3994
  const result = (() => {
3979
3995
  if (!addressRegex.test(address))
3980
3996
  return false;
@@ -3984,7 +4000,7 @@ function isAddress(address, options) {
3984
4000
  return checksumAddress(address) === address;
3985
4001
  return true;
3986
4002
  })();
3987
- isAddressCache.set(address, result);
4003
+ isAddressCache.set(cacheKey, result);
3988
4004
  return result;
3989
4005
  }
3990
4006
 
@@ -4006,6 +4022,7 @@ function toAccount(source) {
4006
4022
  throw new InvalidAddressError({ address: source.address });
4007
4023
  return {
4008
4024
  address: source.address,
4025
+ nonceManager: source.nonceManager,
4009
4026
  signMessage: source.signMessage,
4010
4027
  signTransaction: source.signTransaction,
4011
4028
  signTypedData: source.signTypedData,
@@ -4050,16 +4067,20 @@ function concatHex(values) {
4050
4067
  return `0x${values.reduce((acc, x) => acc + x.replace('0x', ''), '')}`;
4051
4068
  }
4052
4069
 
4053
- function hashMessage(message, to_) {
4054
- const messageBytes = (() => {
4055
- if (typeof message === 'string')
4056
- return stringToBytes(message);
4057
- if (message.raw instanceof Uint8Array)
4058
- return message.raw;
4059
- return toBytes(message.raw);
4070
+ function toPrefixedMessage(message_) {
4071
+ const message = (() => {
4072
+ if (typeof message_ === 'string')
4073
+ return stringToHex(message_);
4074
+ if (typeof message_.raw === 'string')
4075
+ return message_.raw;
4076
+ return bytesToHex(message_.raw);
4060
4077
  })();
4061
- const prefixBytes = stringToBytes(`${presignMessagePrefix}${messageBytes.length}`);
4062
- return keccak256(concat([prefixBytes, messageBytes]), to_);
4078
+ const prefix = stringToHex(`${presignMessagePrefix}${size(message)}`);
4079
+ return concat([prefix, message]);
4080
+ }
4081
+
4082
+ function hashMessage(message, to_) {
4083
+ return keccak256(toPrefixedMessage(message), to_);
4063
4084
  }
4064
4085
 
4065
4086
  /**
@@ -4069,22 +4090,22 @@ function hashMessage(message, to_) {
4069
4090
  * @returns The signature in hex format.
4070
4091
  *
4071
4092
  * @example
4072
- * signatureToHex({
4093
+ * serializeSignature({
4073
4094
  * r: '0x6e100a352ec6ad1b70802290e18aeed190704973570f3b8ed42cb9808e2ea6bf',
4074
4095
  * s: '0x4a90a229a244495b41890987806fcbd2d5d23fc0dbe5f5256c2613c039d76db8',
4075
4096
  * yParity: 1
4076
4097
  * })
4077
4098
  * // "0x6e100a352ec6ad1b70802290e18aeed190704973570f3b8ed42cb9808e2ea6bf4a90a229a244495b41890987806fcbd2d5d23fc0dbe5f5256c2613c039d76db81c"
4078
4099
  */
4079
- function signatureToHex({ r, s, v, yParity }) {
4080
- const vHex = (() => {
4081
- if (v === 27n || yParity === 0)
4082
- return '1b';
4083
- if (v === 28n || yParity === 1)
4084
- return '1c';
4085
- throw new Error('Invalid v value');
4100
+ function serializeSignature({ r, s, v, yParity }) {
4101
+ const yParity_ = (() => {
4102
+ if (yParity === 0 || yParity === 1)
4103
+ return yParity;
4104
+ if (v && (v === 27n || v === 28n || v >= 35n))
4105
+ return v % 2n === 0n ? 1 : 0;
4106
+ throw new Error('Invalid `v` or `yParity` value');
4086
4107
  })();
4087
- return `0x${new secp256k1.Signature(hexToBigInt(r), hexToBigInt(s)).toCompactHex()}${vHex}`;
4108
+ return `0x${new secp256k1.Signature(hexToBigInt(r), hexToBigInt(s)).toCompactHex()}${yParity_ === 0 ? '1b' : '1c'}`;
4088
4109
  }
4089
4110
 
4090
4111
  // TODO(v3): Convert to sync.
@@ -4114,7 +4135,7 @@ async function sign({ hash, privateKey, }) {
4114
4135
  */
4115
4136
  async function signMessage({ message, privateKey, }) {
4116
4137
  const signature = await sign({ hash: hashMessage(message), privateKey });
4117
- return signatureToHex(signature);
4138
+ return serializeSignature(signature);
4118
4139
  }
4119
4140
 
4120
4141
  const gweiUnits = {
@@ -4286,11 +4307,8 @@ function blobsToProofs(parameters) {
4286
4307
  }
4287
4308
 
4288
4309
  function sha256(value, to_) {
4289
- const to = to_ || 'hex';
4290
4310
  const bytes = sha256$1(isHex(value, { strict: false }) ? toBytes(value) : value);
4291
- if (to === 'bytes')
4292
- return bytes;
4293
- return toHex(bytes);
4311
+ return bytes;
4294
4312
  }
4295
4313
 
4296
4314
  /**
@@ -4313,7 +4331,7 @@ function sha256(value, to_) {
4313
4331
  function commitmentToVersionedHash(parameters) {
4314
4332
  const { commitment, version = 1 } = parameters;
4315
4333
  const to = parameters.to ?? (typeof commitment === 'string' ? 'hex' : 'bytes');
4316
- const versionedHash = sha256(commitment, 'bytes');
4334
+ const versionedHash = sha256(commitment);
4317
4335
  versionedHash.set([version], 0);
4318
4336
  return (to === 'bytes' ? versionedHash : bytesToHex(versionedHash));
4319
4337
  }
@@ -4366,7 +4384,6 @@ const maxBytesPerTransaction = bytesPerBlob * blobsPerTransaction -
4366
4384
  1 * fieldElementsPerBlob * blobsPerTransaction;
4367
4385
 
4368
4386
  // https://github.com/ethereum/EIPs/blob/master/EIPS/eip-4844.md#parameters
4369
- /** The number of bytes in a KZG commitment. */
4370
4387
  const versionedHashVersionKzg = 1;
4371
4388
 
4372
4389
  class BlobSizeTooLargeError extends BaseError {
@@ -4463,7 +4480,7 @@ const staticCursor = {
4463
4480
  position: 0,
4464
4481
  positionReadCount: new Map(),
4465
4482
  recursiveReadCount: 0,
4466
- recursiveReadLimit: Infinity,
4483
+ recursiveReadLimit: Number.POSITIVE_INFINITY,
4467
4484
  assertReadLimit() {
4468
4485
  if (this.recursiveReadCount >= this.recursiveReadLimit)
4469
4486
  throw new RecursiveReadLimitExceededError({
@@ -4609,7 +4626,7 @@ const staticCursor = {
4609
4626
  return () => (this.position = oldPosition);
4610
4627
  },
4611
4628
  _touch() {
4612
- if (this.recursiveReadLimit === Infinity)
4629
+ if (this.recursiveReadLimit === Number.POSITIVE_INFINITY)
4613
4630
  return;
4614
4631
  const count = this.getReadCount();
4615
4632
  this.positionReadCount.set(this.position, count + 1);
@@ -4720,83 +4737,153 @@ function toBlobSidecars(parameters) {
4720
4737
  return sidecars;
4721
4738
  }
4722
4739
 
4723
- class AbiEncodingArrayLengthMismatchError extends BaseError {
4724
- constructor({ expectedLength, givenLength, type, }) {
4725
- super([
4726
- `ABI encoding array length mismatch for type ${type}.`,
4727
- `Expected length: ${expectedLength}`,
4728
- `Given length: ${givenLength}`,
4729
- ].join('\n'));
4730
- Object.defineProperty(this, "name", {
4731
- enumerable: true,
4732
- configurable: true,
4733
- writable: true,
4734
- value: 'AbiEncodingArrayLengthMismatchError'
4735
- });
4736
- }
4740
+ function toRlp(bytes, to = 'hex') {
4741
+ const encodable = getEncodable(bytes);
4742
+ const cursor = createCursor(new Uint8Array(encodable.length));
4743
+ encodable.encode(cursor);
4744
+ if (to === 'hex')
4745
+ return bytesToHex(cursor.bytes);
4746
+ return cursor.bytes;
4737
4747
  }
4738
- class AbiEncodingBytesSizeMismatchError extends BaseError {
4739
- constructor({ expectedSize, value }) {
4740
- super(`Size of bytes "${value}" (bytes${size(value)}) does not match expected size (bytes${expectedSize}).`);
4741
- Object.defineProperty(this, "name", {
4742
- enumerable: true,
4743
- configurable: true,
4744
- writable: true,
4745
- value: 'AbiEncodingBytesSizeMismatchError'
4746
- });
4747
- }
4748
+ function getEncodable(bytes) {
4749
+ if (Array.isArray(bytes))
4750
+ return getEncodableList(bytes.map((x) => getEncodable(x)));
4751
+ return getEncodableBytes(bytes);
4748
4752
  }
4749
- class AbiEncodingLengthMismatchError extends BaseError {
4750
- constructor({ expectedLength, givenLength, }) {
4751
- super([
4752
- 'ABI encoding params/values length mismatch.',
4753
- `Expected length (params): ${expectedLength}`,
4754
- `Given length (values): ${givenLength}`,
4755
- ].join('\n'));
4753
+ function getEncodableList(list) {
4754
+ const bodyLength = list.reduce((acc, x) => acc + x.length, 0);
4755
+ const sizeOfBodyLength = getSizeOfLength(bodyLength);
4756
+ const length = (() => {
4757
+ if (bodyLength <= 55)
4758
+ return 1 + bodyLength;
4759
+ return 1 + sizeOfBodyLength + bodyLength;
4760
+ })();
4761
+ return {
4762
+ length,
4763
+ encode(cursor) {
4764
+ if (bodyLength <= 55) {
4765
+ cursor.pushByte(0xc0 + bodyLength);
4766
+ }
4767
+ else {
4768
+ cursor.pushByte(0xc0 + 55 + sizeOfBodyLength);
4769
+ if (sizeOfBodyLength === 1)
4770
+ cursor.pushUint8(bodyLength);
4771
+ else if (sizeOfBodyLength === 2)
4772
+ cursor.pushUint16(bodyLength);
4773
+ else if (sizeOfBodyLength === 3)
4774
+ cursor.pushUint24(bodyLength);
4775
+ else
4776
+ cursor.pushUint32(bodyLength);
4777
+ }
4778
+ for (const { encode } of list) {
4779
+ encode(cursor);
4780
+ }
4781
+ },
4782
+ };
4783
+ }
4784
+ function getEncodableBytes(bytesOrHex) {
4785
+ const bytes = typeof bytesOrHex === 'string' ? hexToBytes(bytesOrHex) : bytesOrHex;
4786
+ const sizeOfBytesLength = getSizeOfLength(bytes.length);
4787
+ const length = (() => {
4788
+ if (bytes.length === 1 && bytes[0] < 0x80)
4789
+ return 1;
4790
+ if (bytes.length <= 55)
4791
+ return 1 + bytes.length;
4792
+ return 1 + sizeOfBytesLength + bytes.length;
4793
+ })();
4794
+ return {
4795
+ length,
4796
+ encode(cursor) {
4797
+ if (bytes.length === 1 && bytes[0] < 0x80) {
4798
+ cursor.pushBytes(bytes);
4799
+ }
4800
+ else if (bytes.length <= 55) {
4801
+ cursor.pushByte(0x80 + bytes.length);
4802
+ cursor.pushBytes(bytes);
4803
+ }
4804
+ else {
4805
+ cursor.pushByte(0x80 + 55 + sizeOfBytesLength);
4806
+ if (sizeOfBytesLength === 1)
4807
+ cursor.pushUint8(bytes.length);
4808
+ else if (sizeOfBytesLength === 2)
4809
+ cursor.pushUint16(bytes.length);
4810
+ else if (sizeOfBytesLength === 3)
4811
+ cursor.pushUint24(bytes.length);
4812
+ else
4813
+ cursor.pushUint32(bytes.length);
4814
+ cursor.pushBytes(bytes);
4815
+ }
4816
+ },
4817
+ };
4818
+ }
4819
+ function getSizeOfLength(length) {
4820
+ if (length < 2 ** 8)
4821
+ return 1;
4822
+ if (length < 2 ** 16)
4823
+ return 2;
4824
+ if (length < 2 ** 24)
4825
+ return 3;
4826
+ if (length < 2 ** 32)
4827
+ return 4;
4828
+ throw new BaseError('Length is too large.');
4829
+ }
4830
+
4831
+ class InvalidChainIdError extends BaseError {
4832
+ constructor({ chainId }) {
4833
+ super(typeof chainId === 'number'
4834
+ ? `Chain ID "${chainId}" is invalid.`
4835
+ : 'Chain ID is invalid.');
4756
4836
  Object.defineProperty(this, "name", {
4757
4837
  enumerable: true,
4758
4838
  configurable: true,
4759
4839
  writable: true,
4760
- value: 'AbiEncodingLengthMismatchError'
4840
+ value: 'InvalidChainIdError'
4761
4841
  });
4762
4842
  }
4763
4843
  }
4764
- class BytesSizeMismatchError extends BaseError {
4765
- constructor({ expectedSize, givenSize, }) {
4766
- super(`Expected bytes${expectedSize}, got bytes${givenSize}.`);
4844
+
4845
+ class FeeCapTooHighError extends BaseError {
4846
+ constructor({ cause, maxFeePerGas, } = {}) {
4847
+ super(`The fee cap (\`maxFeePerGas\`${maxFeePerGas ? ` = ${formatGwei(maxFeePerGas)} gwei` : ''}) cannot be higher than the maximum allowed value (2^256-1).`, {
4848
+ cause,
4849
+ });
4767
4850
  Object.defineProperty(this, "name", {
4768
4851
  enumerable: true,
4769
4852
  configurable: true,
4770
4853
  writable: true,
4771
- value: 'BytesSizeMismatchError'
4854
+ value: 'FeeCapTooHigh'
4772
4855
  });
4773
4856
  }
4774
4857
  }
4775
- class InvalidAbiEncodingTypeError extends BaseError {
4776
- constructor(type, { docsPath }) {
4858
+ Object.defineProperty(FeeCapTooHighError, "nodeMessage", {
4859
+ enumerable: true,
4860
+ configurable: true,
4861
+ writable: true,
4862
+ value: /max fee per gas higher than 2\^256-1|fee cap higher than 2\^256-1/
4863
+ });
4864
+ class TipAboveFeeCapError extends BaseError {
4865
+ constructor({ cause, maxPriorityFeePerGas, maxFeePerGas, } = {}) {
4777
4866
  super([
4778
- `Type "${type}" is not a valid encoding type.`,
4779
- 'Please provide a valid ABI type.',
4780
- ].join('\n'), { docsPath });
4781
- Object.defineProperty(this, "name", {
4782
- enumerable: true,
4783
- configurable: true,
4784
- writable: true,
4785
- value: 'InvalidAbiEncodingType'
4867
+ `The provided tip (\`maxPriorityFeePerGas\`${maxPriorityFeePerGas
4868
+ ? ` = ${formatGwei(maxPriorityFeePerGas)} gwei`
4869
+ : ''}) cannot be higher than the fee cap (\`maxFeePerGas\`${maxFeePerGas ? ` = ${formatGwei(maxFeePerGas)} gwei` : ''}).`,
4870
+ ].join('\n'), {
4871
+ cause,
4786
4872
  });
4787
- }
4788
- }
4789
- class InvalidArrayError extends BaseError {
4790
- constructor(value) {
4791
- super([`Value "${value}" is not a valid array.`].join('\n'));
4792
4873
  Object.defineProperty(this, "name", {
4793
4874
  enumerable: true,
4794
4875
  configurable: true,
4795
4876
  writable: true,
4796
- value: 'InvalidArrayError'
4877
+ value: 'TipAboveFeeCapError'
4797
4878
  });
4798
4879
  }
4799
4880
  }
4881
+ Object.defineProperty(TipAboveFeeCapError, "nodeMessage", {
4882
+ enumerable: true,
4883
+ configurable: true,
4884
+ writable: true,
4885
+ value: /max priority fee per gas higher than max fee per gas|tip higher than fee cap/
4886
+ });
4800
4887
 
4801
4888
  /**
4802
4889
  * @description Returns a section of the hex or byte array given a start/end bytes offset.
@@ -4864,876 +4951,807 @@ function sliceHex(value_, start, end, { strict } = {}) {
4864
4951
  return value;
4865
4952
  }
4866
4953
 
4867
- /**
4868
- * @description Encodes a list of primitive values into an ABI-encoded hex value.
4869
- *
4870
- * - Docs: https://viem.sh/docs/abi/encodeAbiParameters#encodeabiparameters
4871
- *
4872
- * Generates ABI encoded data using the [ABI specification](https://docs.soliditylang.org/en/latest/abi-spec), given a set of ABI parameters (inputs/outputs) and their corresponding values.
4873
- *
4874
- * @param params - a set of ABI Parameters (params), that can be in the shape of the inputs or outputs attribute of an ABI Item.
4875
- * @param values - a set of values (values) that correspond to the given params.
4876
- * @example
4877
- * ```typescript
4878
- * import { encodeAbiParameters } from 'viem'
4879
- *
4880
- * const encodedData = encodeAbiParameters(
4881
- * [
4882
- * { name: 'x', type: 'string' },
4883
- * { name: 'y', type: 'uint' },
4884
- * { name: 'z', type: 'bool' }
4885
- * ],
4886
- * ['wagmi', 420n, true]
4887
- * )
4888
- * ```
4889
- *
4890
- * You can also pass in Human Readable parameters with the parseAbiParameters utility.
4891
- *
4892
- * @example
4893
- * ```typescript
4894
- * import { encodeAbiParameters, parseAbiParameters } from 'viem'
4895
- *
4896
- * const encodedData = encodeAbiParameters(
4897
- * parseAbiParameters('string x, uint y, bool z'),
4898
- * ['wagmi', 420n, true]
4899
- * )
4900
- * ```
4901
- */
4902
- function encodeAbiParameters(params, values) {
4903
- if (params.length !== values.length)
4904
- throw new AbiEncodingLengthMismatchError({
4905
- expectedLength: params.length,
4906
- givenLength: values.length,
4907
- });
4908
- // Prepare the parameters to determine dynamic types to encode.
4909
- const preparedParams = prepareParams({
4910
- params: params,
4911
- values: values,
4912
- });
4913
- const data = encodeParams(preparedParams);
4914
- if (data.length === 0)
4915
- return '0x';
4916
- return data;
4917
- }
4918
- function prepareParams({ params, values, }) {
4919
- const preparedParams = [];
4920
- for (let i = 0; i < params.length; i++) {
4921
- preparedParams.push(prepareParam({ param: params[i], value: values[i] }));
4922
- }
4923
- return preparedParams;
4924
- }
4925
- function prepareParam({ param, value, }) {
4926
- const arrayComponents = getArrayComponents(param.type);
4927
- if (arrayComponents) {
4928
- const [length, type] = arrayComponents;
4929
- return encodeArray(value, { length, param: { ...param, type } });
4930
- }
4931
- if (param.type === 'tuple') {
4932
- return encodeTuple(value, {
4933
- param: param,
4934
- });
4935
- }
4936
- if (param.type === 'address') {
4937
- return encodeAddress(value);
4938
- }
4939
- if (param.type === 'bool') {
4940
- return encodeBool(value);
4941
- }
4942
- if (param.type.startsWith('uint') || param.type.startsWith('int')) {
4943
- const signed = param.type.startsWith('int');
4944
- return encodeNumber(value, { signed });
4954
+ function assertTransactionEIP4844(transaction) {
4955
+ const { blobVersionedHashes } = transaction;
4956
+ if (blobVersionedHashes) {
4957
+ if (blobVersionedHashes.length === 0)
4958
+ throw new EmptyBlobError();
4959
+ for (const hash of blobVersionedHashes) {
4960
+ const size_ = size(hash);
4961
+ const version = hexToNumber(slice(hash, 0, 1));
4962
+ if (size_ !== 32)
4963
+ throw new InvalidVersionedHashSizeError({ hash, size: size_ });
4964
+ if (version !== versionedHashVersionKzg)
4965
+ throw new InvalidVersionedHashVersionError({
4966
+ hash,
4967
+ version,
4968
+ });
4969
+ }
4945
4970
  }
4946
- if (param.type.startsWith('bytes')) {
4947
- return encodeBytes(value, { param });
4971
+ assertTransactionEIP1559(transaction);
4972
+ }
4973
+ function assertTransactionEIP1559(transaction) {
4974
+ const { chainId, maxPriorityFeePerGas, maxFeePerGas, to } = transaction;
4975
+ if (chainId <= 0)
4976
+ throw new InvalidChainIdError({ chainId });
4977
+ if (to && !isAddress(to))
4978
+ throw new InvalidAddressError({ address: to });
4979
+ if (maxFeePerGas && maxFeePerGas > 2n ** 256n - 1n)
4980
+ throw new FeeCapTooHighError({ maxFeePerGas });
4981
+ if (maxPriorityFeePerGas &&
4982
+ maxFeePerGas &&
4983
+ maxPriorityFeePerGas > maxFeePerGas)
4984
+ throw new TipAboveFeeCapError({ maxFeePerGas, maxPriorityFeePerGas });
4985
+ }
4986
+ function assertTransactionEIP2930(transaction) {
4987
+ const { chainId, maxPriorityFeePerGas, gasPrice, maxFeePerGas, to } = transaction;
4988
+ if (chainId <= 0)
4989
+ throw new InvalidChainIdError({ chainId });
4990
+ if (to && !isAddress(to))
4991
+ throw new InvalidAddressError({ address: to });
4992
+ if (maxPriorityFeePerGas || maxFeePerGas)
4993
+ throw new BaseError('`maxFeePerGas`/`maxPriorityFeePerGas` is not a valid EIP-2930 Transaction attribute.');
4994
+ if (gasPrice && gasPrice > 2n ** 256n - 1n)
4995
+ throw new FeeCapTooHighError({ maxFeePerGas: gasPrice });
4996
+ }
4997
+ function assertTransactionLegacy(transaction) {
4998
+ const { chainId, maxPriorityFeePerGas, gasPrice, maxFeePerGas, to, accessList, } = transaction;
4999
+ if (to && !isAddress(to))
5000
+ throw new InvalidAddressError({ address: to });
5001
+ if (typeof chainId !== 'undefined' && chainId <= 0)
5002
+ throw new InvalidChainIdError({ chainId });
5003
+ if (maxPriorityFeePerGas || maxFeePerGas)
5004
+ throw new BaseError('`maxFeePerGas`/`maxPriorityFeePerGas` is not a valid Legacy Transaction attribute.');
5005
+ if (gasPrice && gasPrice > 2n ** 256n - 1n)
5006
+ throw new FeeCapTooHighError({ maxFeePerGas: gasPrice });
5007
+ if (accessList)
5008
+ throw new BaseError('`accessList` is not a valid Legacy Transaction attribute.');
5009
+ }
5010
+
5011
+ function getTransactionType(transaction) {
5012
+ if (transaction.type)
5013
+ return transaction.type;
5014
+ if (typeof transaction.blobs !== 'undefined' ||
5015
+ typeof transaction.blobVersionedHashes !== 'undefined' ||
5016
+ typeof transaction.maxFeePerBlobGas !== 'undefined' ||
5017
+ typeof transaction.sidecars !== 'undefined')
5018
+ return 'eip4844';
5019
+ if (typeof transaction.maxFeePerGas !== 'undefined' ||
5020
+ typeof transaction.maxPriorityFeePerGas !== 'undefined') {
5021
+ return 'eip1559';
4948
5022
  }
4949
- if (param.type === 'string') {
4950
- return encodeString(value);
5023
+ if (typeof transaction.gasPrice !== 'undefined') {
5024
+ if (typeof transaction.accessList !== 'undefined')
5025
+ return 'eip2930';
5026
+ return 'legacy';
4951
5027
  }
4952
- throw new InvalidAbiEncodingTypeError(param.type, {
4953
- docsPath: '/docs/contract/encodeAbiParameters',
4954
- });
5028
+ throw new InvalidSerializableTransactionError({ transaction });
4955
5029
  }
4956
- function encodeParams(preparedParams) {
4957
- // 1. Compute the size of the static part of the parameters.
4958
- let staticSize = 0;
4959
- for (let i = 0; i < preparedParams.length; i++) {
4960
- const { dynamic, encoded } = preparedParams[i];
4961
- if (dynamic)
4962
- staticSize += 32;
4963
- else
4964
- staticSize += size(encoded);
4965
- }
4966
- // 2. Split the parameters into static and dynamic parts.
4967
- const staticParams = [];
4968
- const dynamicParams = [];
4969
- let dynamicSize = 0;
4970
- for (let i = 0; i < preparedParams.length; i++) {
4971
- const { dynamic, encoded } = preparedParams[i];
4972
- if (dynamic) {
4973
- staticParams.push(numberToHex(staticSize + dynamicSize, { size: 32 }));
4974
- dynamicParams.push(encoded);
4975
- dynamicSize += size(encoded);
5030
+
5031
+ /*
5032
+ * Serialize an EIP-2930 access list
5033
+ * @remarks
5034
+ * Use to create a transaction serializer with support for EIP-2930 access lists
5035
+ *
5036
+ * @param accessList - Array of objects of address and arrays of Storage Keys
5037
+ * @throws InvalidAddressError, InvalidStorageKeySizeError
5038
+ * @returns Array of hex strings
5039
+ */
5040
+ function serializeAccessList(accessList) {
5041
+ if (!accessList || accessList.length === 0)
5042
+ return [];
5043
+ const serializedAccessList = [];
5044
+ for (let i = 0; i < accessList.length; i++) {
5045
+ const { address, storageKeys } = accessList[i];
5046
+ for (let j = 0; j < storageKeys.length; j++) {
5047
+ if (storageKeys[j].length - 2 !== 64) {
5048
+ throw new InvalidStorageKeySizeError({ storageKey: storageKeys[j] });
5049
+ }
4976
5050
  }
4977
- else {
4978
- staticParams.push(encoded);
5051
+ if (!isAddress(address, { strict: false })) {
5052
+ throw new InvalidAddressError({ address });
4979
5053
  }
5054
+ serializedAccessList.push([address, storageKeys]);
4980
5055
  }
4981
- // 3. Concatenate static and dynamic parts.
4982
- return concat([...staticParams, ...dynamicParams]);
5056
+ return serializedAccessList;
4983
5057
  }
4984
- function encodeAddress(value) {
4985
- if (!isAddress(value))
4986
- throw new InvalidAddressError({ address: value });
4987
- return { dynamic: false, encoded: padHex(value.toLowerCase()) };
5058
+
5059
+ function serializeTransaction(transaction, signature) {
5060
+ const type = getTransactionType(transaction);
5061
+ if (type === 'eip1559')
5062
+ return serializeTransactionEIP1559(transaction, signature);
5063
+ if (type === 'eip2930')
5064
+ return serializeTransactionEIP2930(transaction, signature);
5065
+ if (type === 'eip4844')
5066
+ return serializeTransactionEIP4844(transaction, signature);
5067
+ return serializeTransactionLegacy(transaction, signature);
4988
5068
  }
4989
- function encodeArray(value, { length, param, }) {
4990
- const dynamic = length === null;
4991
- if (!Array.isArray(value))
4992
- throw new InvalidArrayError(value);
4993
- if (!dynamic && value.length !== length)
4994
- throw new AbiEncodingArrayLengthMismatchError({
4995
- expectedLength: length,
4996
- givenLength: value.length,
4997
- type: `${param.type}[${length}]`,
5069
+ function serializeTransactionEIP4844(transaction, signature) {
5070
+ const { chainId, gas, nonce, to, value, maxFeePerBlobGas, maxFeePerGas, maxPriorityFeePerGas, accessList, data, } = transaction;
5071
+ assertTransactionEIP4844(transaction);
5072
+ let blobVersionedHashes = transaction.blobVersionedHashes;
5073
+ let sidecars = transaction.sidecars;
5074
+ // If `blobs` are passed, we will need to compute the KZG commitments & proofs.
5075
+ if (transaction.blobs &&
5076
+ (typeof blobVersionedHashes === 'undefined' ||
5077
+ typeof sidecars === 'undefined')) {
5078
+ const blobs = (typeof transaction.blobs[0] === 'string'
5079
+ ? transaction.blobs
5080
+ : transaction.blobs.map((x) => bytesToHex(x)));
5081
+ const kzg = transaction.kzg;
5082
+ const commitments = blobsToCommitments({
5083
+ blobs,
5084
+ kzg,
4998
5085
  });
4999
- let dynamicChild = false;
5000
- const preparedParams = [];
5001
- for (let i = 0; i < value.length; i++) {
5002
- const preparedParam = prepareParam({ param, value: value[i] });
5003
- if (preparedParam.dynamic)
5004
- dynamicChild = true;
5005
- preparedParams.push(preparedParam);
5006
- }
5007
- if (dynamic || dynamicChild) {
5008
- const data = encodeParams(preparedParams);
5009
- if (dynamic) {
5010
- const length = numberToHex(preparedParams.length, { size: 32 });
5011
- return {
5012
- dynamic: true,
5013
- encoded: preparedParams.length > 0 ? concat([length, data]) : length,
5014
- };
5086
+ if (typeof blobVersionedHashes === 'undefined')
5087
+ blobVersionedHashes = commitmentsToVersionedHashes({
5088
+ commitments,
5089
+ });
5090
+ if (typeof sidecars === 'undefined') {
5091
+ const proofs = blobsToProofs({ blobs, commitments, kzg });
5092
+ sidecars = toBlobSidecars({ blobs, commitments, proofs });
5015
5093
  }
5016
- if (dynamicChild)
5017
- return { dynamic: true, encoded: data };
5018
5094
  }
5019
- return {
5020
- dynamic: false,
5021
- encoded: concat(preparedParams.map(({ encoded }) => encoded)),
5022
- };
5095
+ const serializedAccessList = serializeAccessList(accessList);
5096
+ const serializedTransaction = [
5097
+ toHex(chainId),
5098
+ nonce ? toHex(nonce) : '0x',
5099
+ maxPriorityFeePerGas ? toHex(maxPriorityFeePerGas) : '0x',
5100
+ maxFeePerGas ? toHex(maxFeePerGas) : '0x',
5101
+ gas ? toHex(gas) : '0x',
5102
+ to ?? '0x',
5103
+ value ? toHex(value) : '0x',
5104
+ data ?? '0x',
5105
+ serializedAccessList,
5106
+ maxFeePerBlobGas ? toHex(maxFeePerBlobGas) : '0x',
5107
+ blobVersionedHashes ?? [],
5108
+ ...toYParitySignatureArray(transaction, signature),
5109
+ ];
5110
+ const blobs = [];
5111
+ const commitments = [];
5112
+ const proofs = [];
5113
+ if (sidecars)
5114
+ for (let i = 0; i < sidecars.length; i++) {
5115
+ const { blob, commitment, proof } = sidecars[i];
5116
+ blobs.push(blob);
5117
+ commitments.push(commitment);
5118
+ proofs.push(proof);
5119
+ }
5120
+ return concatHex([
5121
+ '0x03',
5122
+ sidecars
5123
+ ? // If sidecars are enabled, envelope turns into a "wrapper":
5124
+ toRlp([serializedTransaction, blobs, commitments, proofs])
5125
+ : // If sidecars are disabled, standard envelope is used:
5126
+ toRlp(serializedTransaction),
5127
+ ]);
5023
5128
  }
5024
- function encodeBytes(value, { param }) {
5025
- const [, paramSize] = param.type.split('bytes');
5026
- const bytesSize = size(value);
5027
- if (!paramSize) {
5028
- let value_ = value;
5029
- // If the size is not divisible by 32 bytes, pad the end
5030
- // with empty bytes to the ceiling 32 bytes.
5031
- if (bytesSize % 32 !== 0)
5032
- value_ = padHex(value_, {
5033
- dir: 'right',
5034
- size: Math.ceil((value.length - 2) / 2 / 32) * 32,
5035
- });
5036
- return {
5037
- dynamic: true,
5038
- encoded: concat([padHex(numberToHex(bytesSize, { size: 32 })), value_]),
5039
- };
5040
- }
5041
- if (bytesSize !== parseInt(paramSize))
5042
- throw new AbiEncodingBytesSizeMismatchError({
5043
- expectedSize: parseInt(paramSize),
5044
- value,
5045
- });
5046
- return { dynamic: false, encoded: padHex(value, { dir: 'right' }) };
5047
- }
5048
- function encodeBool(value) {
5049
- if (typeof value !== 'boolean')
5050
- throw new BaseError(`Invalid boolean value: "${value}" (type: ${typeof value}). Expected: \`true\` or \`false\`.`);
5051
- return { dynamic: false, encoded: padHex(boolToHex(value)) };
5129
+ function serializeTransactionEIP1559(transaction, signature) {
5130
+ const { chainId, gas, nonce, to, value, maxFeePerGas, maxPriorityFeePerGas, accessList, data, } = transaction;
5131
+ assertTransactionEIP1559(transaction);
5132
+ const serializedAccessList = serializeAccessList(accessList);
5133
+ const serializedTransaction = [
5134
+ toHex(chainId),
5135
+ nonce ? toHex(nonce) : '0x',
5136
+ maxPriorityFeePerGas ? toHex(maxPriorityFeePerGas) : '0x',
5137
+ maxFeePerGas ? toHex(maxFeePerGas) : '0x',
5138
+ gas ? toHex(gas) : '0x',
5139
+ to ?? '0x',
5140
+ value ? toHex(value) : '0x',
5141
+ data ?? '0x',
5142
+ serializedAccessList,
5143
+ ...toYParitySignatureArray(transaction, signature),
5144
+ ];
5145
+ return concatHex([
5146
+ '0x02',
5147
+ toRlp(serializedTransaction),
5148
+ ]);
5052
5149
  }
5053
- function encodeNumber(value, { signed }) {
5054
- return {
5055
- dynamic: false,
5056
- encoded: numberToHex(value, {
5057
- size: 32,
5058
- signed,
5059
- }),
5060
- };
5150
+ function serializeTransactionEIP2930(transaction, signature) {
5151
+ const { chainId, gas, data, nonce, to, value, accessList, gasPrice } = transaction;
5152
+ assertTransactionEIP2930(transaction);
5153
+ const serializedAccessList = serializeAccessList(accessList);
5154
+ const serializedTransaction = [
5155
+ toHex(chainId),
5156
+ nonce ? toHex(nonce) : '0x',
5157
+ gasPrice ? toHex(gasPrice) : '0x',
5158
+ gas ? toHex(gas) : '0x',
5159
+ to ?? '0x',
5160
+ value ? toHex(value) : '0x',
5161
+ data ?? '0x',
5162
+ serializedAccessList,
5163
+ ...toYParitySignatureArray(transaction, signature),
5164
+ ];
5165
+ return concatHex([
5166
+ '0x01',
5167
+ toRlp(serializedTransaction),
5168
+ ]);
5061
5169
  }
5062
- function encodeString(value) {
5063
- const hexValue = stringToHex(value);
5064
- const partsLength = Math.ceil(size(hexValue) / 32);
5065
- const parts = [];
5066
- for (let i = 0; i < partsLength; i++) {
5067
- parts.push(padHex(slice(hexValue, i * 32, (i + 1) * 32), {
5068
- dir: 'right',
5069
- }));
5170
+ function serializeTransactionLegacy(transaction, signature) {
5171
+ const { chainId = 0, gas, data, nonce, to, value, gasPrice } = transaction;
5172
+ assertTransactionLegacy(transaction);
5173
+ let serializedTransaction = [
5174
+ nonce ? toHex(nonce) : '0x',
5175
+ gasPrice ? toHex(gasPrice) : '0x',
5176
+ gas ? toHex(gas) : '0x',
5177
+ to ?? '0x',
5178
+ value ? toHex(value) : '0x',
5179
+ data ?? '0x',
5180
+ ];
5181
+ if (signature) {
5182
+ const v = (() => {
5183
+ // EIP-155 (inferred chainId)
5184
+ if (signature.v >= 35n) {
5185
+ const inferredChainId = (signature.v - 35n) / 2n;
5186
+ if (inferredChainId > 0)
5187
+ return signature.v;
5188
+ return 27n + (signature.v === 35n ? 0n : 1n);
5189
+ }
5190
+ // EIP-155 (explicit chainId)
5191
+ if (chainId > 0)
5192
+ return BigInt(chainId * 2) + BigInt(35n + signature.v - 27n);
5193
+ // Pre-EIP-155 (no chainId)
5194
+ const v = 27n + (signature.v === 27n ? 0n : 1n);
5195
+ if (signature.v !== v)
5196
+ throw new InvalidLegacyVError({ v: signature.v });
5197
+ return v;
5198
+ })();
5199
+ serializedTransaction = [
5200
+ ...serializedTransaction,
5201
+ toHex(v),
5202
+ signature.r,
5203
+ signature.s,
5204
+ ];
5070
5205
  }
5071
- return {
5072
- dynamic: true,
5073
- encoded: concat([
5074
- padHex(numberToHex(size(hexValue), { size: 32 })),
5075
- ...parts,
5076
- ]),
5077
- };
5078
- }
5079
- function encodeTuple(value, { param }) {
5080
- let dynamic = false;
5081
- const preparedParams = [];
5082
- for (let i = 0; i < param.components.length; i++) {
5083
- const param_ = param.components[i];
5084
- const index = Array.isArray(value) ? i : param_.name;
5085
- const preparedParam = prepareParam({
5086
- param: param_,
5087
- value: value[index],
5088
- });
5089
- preparedParams.push(preparedParam);
5090
- if (preparedParam.dynamic)
5091
- dynamic = true;
5206
+ else if (chainId > 0) {
5207
+ serializedTransaction = [
5208
+ ...serializedTransaction,
5209
+ toHex(chainId),
5210
+ '0x',
5211
+ '0x',
5212
+ ];
5092
5213
  }
5093
- return {
5094
- dynamic,
5095
- encoded: dynamic
5096
- ? encodeParams(preparedParams)
5097
- : concat(preparedParams.map(({ encoded }) => encoded)),
5098
- };
5214
+ return toRlp(serializedTransaction);
5099
5215
  }
5100
- function getArrayComponents(type) {
5101
- const matches = type.match(/^(.*)\[(\d+)?\]$/);
5102
- return matches
5103
- ? // Return `null` if the array is dynamic.
5104
- [matches[2] ? Number(matches[2]) : null, matches[1]]
5105
- : undefined;
5216
+ function toYParitySignatureArray(transaction, signature_) {
5217
+ const signature = signature_ ?? transaction;
5218
+ const { v, yParity } = signature;
5219
+ if (typeof signature.r === 'undefined')
5220
+ return [];
5221
+ if (typeof signature.s === 'undefined')
5222
+ return [];
5223
+ if (typeof v === 'undefined' && typeof yParity === 'undefined')
5224
+ return [];
5225
+ const r = trim(signature.r);
5226
+ const s = trim(signature.s);
5227
+ const yParity_ = (() => {
5228
+ if (typeof yParity === 'number')
5229
+ return yParity ? toHex(1) : '0x';
5230
+ if (v === 0n)
5231
+ return '0x';
5232
+ if (v === 1n)
5233
+ return toHex(1);
5234
+ return v === 27n ? '0x' : toHex(1);
5235
+ })();
5236
+ return [yParity_, r === '0x00' ? '0x' : r, s === '0x00' ? '0x' : s];
5106
5237
  }
5107
5238
 
5108
- class FeeCapTooHighError extends BaseError {
5109
- constructor({ cause, maxFeePerGas, } = {}) {
5110
- super(`The fee cap (\`maxFeePerGas\`${maxFeePerGas ? ` = ${formatGwei(maxFeePerGas)} gwei` : ''}) cannot be higher than the maximum allowed value (2^256-1).`, {
5111
- cause,
5112
- });
5239
+ async function signTransaction(parameters) {
5240
+ const { privateKey, transaction, serializer = serializeTransaction, } = parameters;
5241
+ const signableTransaction = (() => {
5242
+ // For EIP-4844 Transactions, we want to sign the transaction payload body (tx_payload_body) without the sidecars (ie. without the network wrapper).
5243
+ // See: https://github.com/ethereum/EIPs/blob/e00f4daa66bd56e2dbd5f1d36d09fd613811a48b/EIPS/eip-4844.md#networking
5244
+ if (transaction.type === 'eip4844')
5245
+ return {
5246
+ ...transaction,
5247
+ sidecars: false,
5248
+ };
5249
+ return transaction;
5250
+ })();
5251
+ const signature = await sign({
5252
+ hash: keccak256(serializer(signableTransaction)),
5253
+ privateKey,
5254
+ });
5255
+ return serializer(transaction, signature);
5256
+ }
5257
+
5258
+ class AbiEncodingArrayLengthMismatchError extends BaseError {
5259
+ constructor({ expectedLength, givenLength, type, }) {
5260
+ super([
5261
+ `ABI encoding array length mismatch for type ${type}.`,
5262
+ `Expected length: ${expectedLength}`,
5263
+ `Given length: ${givenLength}`,
5264
+ ].join('\n'));
5113
5265
  Object.defineProperty(this, "name", {
5114
5266
  enumerable: true,
5115
5267
  configurable: true,
5116
5268
  writable: true,
5117
- value: 'FeeCapTooHigh'
5269
+ value: 'AbiEncodingArrayLengthMismatchError'
5118
5270
  });
5119
5271
  }
5120
5272
  }
5121
- Object.defineProperty(FeeCapTooHighError, "nodeMessage", {
5122
- enumerable: true,
5123
- configurable: true,
5124
- writable: true,
5125
- value: /max fee per gas higher than 2\^256-1|fee cap higher than 2\^256-1/
5126
- });
5127
- class TipAboveFeeCapError extends BaseError {
5128
- constructor({ cause, maxPriorityFeePerGas, maxFeePerGas, } = {}) {
5129
- super([
5130
- `The provided tip (\`maxPriorityFeePerGas\`${maxPriorityFeePerGas
5131
- ? ` = ${formatGwei(maxPriorityFeePerGas)} gwei`
5132
- : ''}) cannot be higher than the fee cap (\`maxFeePerGas\`${maxFeePerGas ? ` = ${formatGwei(maxFeePerGas)} gwei` : ''}).`,
5133
- ].join('\n'), {
5134
- cause,
5135
- });
5273
+ class AbiEncodingBytesSizeMismatchError extends BaseError {
5274
+ constructor({ expectedSize, value }) {
5275
+ super(`Size of bytes "${value}" (bytes${size(value)}) does not match expected size (bytes${expectedSize}).`);
5136
5276
  Object.defineProperty(this, "name", {
5137
5277
  enumerable: true,
5138
5278
  configurable: true,
5139
5279
  writable: true,
5140
- value: 'TipAboveFeeCapError'
5280
+ value: 'AbiEncodingBytesSizeMismatchError'
5141
5281
  });
5142
5282
  }
5143
5283
  }
5144
- Object.defineProperty(TipAboveFeeCapError, "nodeMessage", {
5145
- enumerable: true,
5146
- configurable: true,
5147
- writable: true,
5148
- value: /max priority fee per gas higher than max fee per gas|tip higher than fee cap/
5149
- });
5150
-
5151
- function getTransactionType(transaction) {
5152
- if (transaction.type)
5153
- return transaction.type;
5154
- if (typeof transaction.blobs !== 'undefined' ||
5155
- typeof transaction.blobVersionedHashes !== 'undefined' ||
5156
- typeof transaction.maxFeePerBlobGas !== 'undefined' ||
5157
- typeof transaction.sidecars !== 'undefined')
5158
- return 'eip4844';
5159
- if (typeof transaction.maxFeePerGas !== 'undefined' ||
5160
- typeof transaction.maxPriorityFeePerGas !== 'undefined') {
5161
- return 'eip1559';
5162
- }
5163
- if (typeof transaction.gasPrice !== 'undefined') {
5164
- if (typeof transaction.accessList !== 'undefined')
5165
- return 'eip2930';
5166
- return 'legacy';
5284
+ class AbiEncodingLengthMismatchError extends BaseError {
5285
+ constructor({ expectedLength, givenLength, }) {
5286
+ super([
5287
+ 'ABI encoding params/values length mismatch.',
5288
+ `Expected length (params): ${expectedLength}`,
5289
+ `Given length (values): ${givenLength}`,
5290
+ ].join('\n'));
5291
+ Object.defineProperty(this, "name", {
5292
+ enumerable: true,
5293
+ configurable: true,
5294
+ writable: true,
5295
+ value: 'AbiEncodingLengthMismatchError'
5296
+ });
5167
5297
  }
5168
- throw new InvalidSerializableTransactionError({ transaction });
5169
5298
  }
5170
-
5171
- class InvalidChainIdError extends BaseError {
5172
- constructor({ chainId }) {
5173
- super(typeof chainId === 'number'
5174
- ? `Chain ID "${chainId}" is invalid.`
5175
- : 'Chain ID is invalid.');
5299
+ class BytesSizeMismatchError extends BaseError {
5300
+ constructor({ expectedSize, givenSize, }) {
5301
+ super(`Expected bytes${expectedSize}, got bytes${givenSize}.`);
5176
5302
  Object.defineProperty(this, "name", {
5177
5303
  enumerable: true,
5178
5304
  configurable: true,
5179
5305
  writable: true,
5180
- value: 'InvalidChainIdError'
5306
+ value: 'BytesSizeMismatchError'
5181
5307
  });
5182
5308
  }
5183
5309
  }
5184
-
5185
- // `bytes<M>`: binary type of `M` bytes, `0 < M <= 32`
5186
- // https://regexr.com/6va55
5187
- const bytesRegex = /^bytes([1-9]|1[0-9]|2[0-9]|3[0-2])?$/;
5188
- // `(u)int<M>`: (un)signed integer type of `M` bits, `0 < M <= 256`, `M % 8 == 0`
5189
- // https://regexr.com/6v8hp
5190
- const integerRegex = /^(u?int)(8|16|24|32|40|48|56|64|72|80|88|96|104|112|120|128|136|144|152|160|168|176|184|192|200|208|216|224|232|240|248|256)?$/;
5191
-
5192
- // Implementation forked and adapted from https://github.com/MetaMask/eth-sig-util/blob/main/src/sign-typed-data.ts
5193
- function hashTypedData(parameters) {
5194
- const { domain = {}, message, primaryType, } = parameters;
5195
- const types = {
5196
- EIP712Domain: getTypesForEIP712Domain({ domain }),
5197
- ...parameters.types,
5198
- };
5199
- // Need to do a runtime validation check on addresses, byte ranges, integer ranges, etc
5200
- // as we can't statically check this with TypeScript.
5201
- validateTypedData({
5202
- domain,
5203
- message,
5204
- primaryType,
5205
- types,
5206
- });
5207
- const parts = ['0x1901'];
5208
- if (domain)
5209
- parts.push(hashDomain({
5210
- domain,
5211
- types: types,
5212
- }));
5213
- if (primaryType !== 'EIP712Domain')
5214
- parts.push(hashStruct({
5215
- data: message,
5216
- primaryType,
5217
- types: types,
5218
- }));
5219
- return keccak256(concat(parts));
5220
- }
5221
- function hashDomain({ domain, types, }) {
5222
- return hashStruct({
5223
- data: domain,
5224
- primaryType: 'EIP712Domain',
5225
- types,
5226
- });
5227
- }
5228
- function hashStruct({ data, primaryType, types, }) {
5229
- const encoded = encodeData({
5230
- data,
5231
- primaryType,
5232
- types,
5233
- });
5234
- return keccak256(encoded);
5310
+ class InvalidAbiEncodingTypeError extends BaseError {
5311
+ constructor(type, { docsPath }) {
5312
+ super([
5313
+ `Type "${type}" is not a valid encoding type.`,
5314
+ 'Please provide a valid ABI type.',
5315
+ ].join('\n'), { docsPath });
5316
+ Object.defineProperty(this, "name", {
5317
+ enumerable: true,
5318
+ configurable: true,
5319
+ writable: true,
5320
+ value: 'InvalidAbiEncodingType'
5321
+ });
5322
+ }
5235
5323
  }
5236
- function encodeData({ data, primaryType, types, }) {
5237
- const encodedTypes = [{ type: 'bytes32' }];
5238
- const encodedValues = [hashType({ primaryType, types })];
5239
- for (const field of types[primaryType]) {
5240
- const [type, value] = encodeField({
5241
- types,
5242
- name: field.name,
5243
- type: field.type,
5244
- value: data[field.name],
5324
+ class InvalidArrayError extends BaseError {
5325
+ constructor(value) {
5326
+ super([`Value "${value}" is not a valid array.`].join('\n'));
5327
+ Object.defineProperty(this, "name", {
5328
+ enumerable: true,
5329
+ configurable: true,
5330
+ writable: true,
5331
+ value: 'InvalidArrayError'
5245
5332
  });
5246
- encodedTypes.push(type);
5247
- encodedValues.push(value);
5248
5333
  }
5249
- return encodeAbiParameters(encodedTypes, encodedValues);
5250
5334
  }
5251
- function hashType({ primaryType, types, }) {
5252
- const encodedHashType = toHex(encodeType({ primaryType, types }));
5253
- return keccak256(encodedHashType);
5335
+
5336
+ /**
5337
+ * @description Encodes a list of primitive values into an ABI-encoded hex value.
5338
+ *
5339
+ * - Docs: https://viem.sh/docs/abi/encodeAbiParameters#encodeabiparameters
5340
+ *
5341
+ * Generates ABI encoded data using the [ABI specification](https://docs.soliditylang.org/en/latest/abi-spec), given a set of ABI parameters (inputs/outputs) and their corresponding values.
5342
+ *
5343
+ * @param params - a set of ABI Parameters (params), that can be in the shape of the inputs or outputs attribute of an ABI Item.
5344
+ * @param values - a set of values (values) that correspond to the given params.
5345
+ * @example
5346
+ * ```typescript
5347
+ * import { encodeAbiParameters } from 'viem'
5348
+ *
5349
+ * const encodedData = encodeAbiParameters(
5350
+ * [
5351
+ * { name: 'x', type: 'string' },
5352
+ * { name: 'y', type: 'uint' },
5353
+ * { name: 'z', type: 'bool' }
5354
+ * ],
5355
+ * ['wagmi', 420n, true]
5356
+ * )
5357
+ * ```
5358
+ *
5359
+ * You can also pass in Human Readable parameters with the parseAbiParameters utility.
5360
+ *
5361
+ * @example
5362
+ * ```typescript
5363
+ * import { encodeAbiParameters, parseAbiParameters } from 'viem'
5364
+ *
5365
+ * const encodedData = encodeAbiParameters(
5366
+ * parseAbiParameters('string x, uint y, bool z'),
5367
+ * ['wagmi', 420n, true]
5368
+ * )
5369
+ * ```
5370
+ */
5371
+ function encodeAbiParameters(params, values) {
5372
+ if (params.length !== values.length)
5373
+ throw new AbiEncodingLengthMismatchError({
5374
+ expectedLength: params.length,
5375
+ givenLength: values.length,
5376
+ });
5377
+ // Prepare the parameters to determine dynamic types to encode.
5378
+ const preparedParams = prepareParams({
5379
+ params: params,
5380
+ values: values,
5381
+ });
5382
+ const data = encodeParams(preparedParams);
5383
+ if (data.length === 0)
5384
+ return '0x';
5385
+ return data;
5254
5386
  }
5255
- function encodeType({ primaryType, types, }) {
5256
- let result = '';
5257
- const unsortedDeps = findTypeDependencies({ primaryType, types });
5258
- unsortedDeps.delete(primaryType);
5259
- const deps = [primaryType, ...Array.from(unsortedDeps).sort()];
5260
- for (const type of deps) {
5261
- result += `${type}(${types[type]
5262
- .map(({ name, type: t }) => `${t} ${name}`)
5263
- .join(',')})`;
5387
+ function prepareParams({ params, values, }) {
5388
+ const preparedParams = [];
5389
+ for (let i = 0; i < params.length; i++) {
5390
+ preparedParams.push(prepareParam({ param: params[i], value: values[i] }));
5264
5391
  }
5265
- return result;
5392
+ return preparedParams;
5266
5393
  }
5267
- function findTypeDependencies({ primaryType: primaryType_, types, }, results = new Set()) {
5268
- const match = primaryType_.match(/^\w*/u);
5269
- const primaryType = match?.[0];
5270
- if (results.has(primaryType) || types[primaryType] === undefined) {
5271
- return results;
5394
+ function prepareParam({ param, value, }) {
5395
+ const arrayComponents = getArrayComponents(param.type);
5396
+ if (arrayComponents) {
5397
+ const [length, type] = arrayComponents;
5398
+ return encodeArray(value, { length, param: { ...param, type } });
5272
5399
  }
5273
- results.add(primaryType);
5274
- for (const field of types[primaryType]) {
5275
- findTypeDependencies({ primaryType: field.type, types }, results);
5400
+ if (param.type === 'tuple') {
5401
+ return encodeTuple(value, {
5402
+ param: param,
5403
+ });
5276
5404
  }
5277
- return results;
5278
- }
5279
- function encodeField({ types, name, type, value, }) {
5280
- if (types[type] !== undefined) {
5281
- return [
5282
- { type: 'bytes32' },
5283
- keccak256(encodeData({ data: value, primaryType: type, types })),
5284
- ];
5405
+ if (param.type === 'address') {
5406
+ return encodeAddress(value);
5285
5407
  }
5286
- if (type === 'bytes') {
5287
- const prepend = value.length % 2 ? '0' : '';
5288
- value = `0x${prepend + value.slice(2)}`;
5289
- return [{ type: 'bytes32' }, keccak256(value)];
5408
+ if (param.type === 'bool') {
5409
+ return encodeBool(value);
5290
5410
  }
5291
- if (type === 'string')
5292
- return [{ type: 'bytes32' }, keccak256(toHex(value))];
5293
- if (type.lastIndexOf(']') === type.length - 1) {
5294
- const parsedType = type.slice(0, type.lastIndexOf('['));
5295
- const typeValuePairs = value.map((item) => encodeField({
5296
- name,
5297
- type: parsedType,
5298
- types,
5299
- value: item,
5300
- }));
5301
- return [
5302
- { type: 'bytes32' },
5303
- keccak256(encodeAbiParameters(typeValuePairs.map(([t]) => t), typeValuePairs.map(([, v]) => v))),
5304
- ];
5411
+ if (param.type.startsWith('uint') || param.type.startsWith('int')) {
5412
+ const signed = param.type.startsWith('int');
5413
+ return encodeNumber(value, { signed });
5305
5414
  }
5306
- return [{ type }, value];
5307
- }
5308
-
5309
- function validateTypedData(parameters) {
5310
- const { domain, message, primaryType, types } = parameters;
5311
- const validateData = (struct, data) => {
5312
- for (const param of struct) {
5313
- const { name, type } = param;
5314
- const value = data[name];
5315
- const integerMatch = type.match(integerRegex);
5316
- if (integerMatch &&
5317
- (typeof value === 'number' || typeof value === 'bigint')) {
5318
- const [_type, base, size_] = integerMatch;
5319
- // If number cannot be cast to a sized hex value, it is out of range
5320
- // and will throw.
5321
- numberToHex(value, {
5322
- signed: base === 'int',
5323
- size: parseInt(size_) / 8,
5324
- });
5325
- }
5326
- if (type === 'address' && typeof value === 'string' && !isAddress(value))
5327
- throw new InvalidAddressError({ address: value });
5328
- const bytesMatch = type.match(bytesRegex);
5329
- if (bytesMatch) {
5330
- const [_type, size_] = bytesMatch;
5331
- if (size_ && size(value) !== parseInt(size_))
5332
- throw new BytesSizeMismatchError({
5333
- expectedSize: parseInt(size_),
5334
- givenSize: size(value),
5335
- });
5336
- }
5337
- const struct = types[type];
5338
- if (struct)
5339
- validateData(struct, value);
5340
- }
5341
- };
5342
- // Validate domain types.
5343
- if (types.EIP712Domain && domain)
5344
- validateData(types.EIP712Domain, domain);
5345
- if (primaryType !== 'EIP712Domain') {
5346
- // Validate message types.
5347
- const type = types[primaryType];
5348
- validateData(type, message);
5415
+ if (param.type.startsWith('bytes')) {
5416
+ return encodeBytes(value, { param });
5417
+ }
5418
+ if (param.type === 'string') {
5419
+ return encodeString(value);
5349
5420
  }
5421
+ throw new InvalidAbiEncodingTypeError(param.type, {
5422
+ docsPath: '/docs/contract/encodeAbiParameters',
5423
+ });
5350
5424
  }
5351
- function getTypesForEIP712Domain({ domain, }) {
5352
- return [
5353
- typeof domain?.name === 'string' && { name: 'name', type: 'string' },
5354
- domain?.version && { name: 'version', type: 'string' },
5355
- typeof domain?.chainId === 'number' && {
5356
- name: 'chainId',
5357
- type: 'uint256',
5358
- },
5359
- domain?.verifyingContract && {
5360
- name: 'verifyingContract',
5361
- type: 'address',
5362
- },
5363
- domain?.salt && { name: 'salt', type: 'bytes32' },
5364
- ].filter(Boolean);
5365
- }
5366
-
5367
- function assertTransactionEIP4844(transaction) {
5368
- const { blobVersionedHashes } = transaction;
5369
- if (blobVersionedHashes) {
5370
- if (blobVersionedHashes.length === 0)
5371
- throw new EmptyBlobError();
5372
- for (const hash of blobVersionedHashes) {
5373
- const size_ = size(hash);
5374
- const version = hexToNumber(slice(hash, 0, 1));
5375
- if (size_ !== 32)
5376
- throw new InvalidVersionedHashSizeError({ hash, size: size_ });
5377
- if (version !== versionedHashVersionKzg)
5378
- throw new InvalidVersionedHashVersionError({
5379
- hash,
5380
- version,
5381
- });
5425
+ function encodeParams(preparedParams) {
5426
+ // 1. Compute the size of the static part of the parameters.
5427
+ let staticSize = 0;
5428
+ for (let i = 0; i < preparedParams.length; i++) {
5429
+ const { dynamic, encoded } = preparedParams[i];
5430
+ if (dynamic)
5431
+ staticSize += 32;
5432
+ else
5433
+ staticSize += size(encoded);
5434
+ }
5435
+ // 2. Split the parameters into static and dynamic parts.
5436
+ const staticParams = [];
5437
+ const dynamicParams = [];
5438
+ let dynamicSize = 0;
5439
+ for (let i = 0; i < preparedParams.length; i++) {
5440
+ const { dynamic, encoded } = preparedParams[i];
5441
+ if (dynamic) {
5442
+ staticParams.push(numberToHex(staticSize + dynamicSize, { size: 32 }));
5443
+ dynamicParams.push(encoded);
5444
+ dynamicSize += size(encoded);
5445
+ }
5446
+ else {
5447
+ staticParams.push(encoded);
5382
5448
  }
5383
5449
  }
5384
- assertTransactionEIP1559(transaction);
5385
- }
5386
- function assertTransactionEIP1559(transaction) {
5387
- const { chainId, maxPriorityFeePerGas, maxFeePerGas, to } = transaction;
5388
- if (chainId <= 0)
5389
- throw new InvalidChainIdError({ chainId });
5390
- if (to && !isAddress(to))
5391
- throw new InvalidAddressError({ address: to });
5392
- if (maxFeePerGas && maxFeePerGas > 2n ** 256n - 1n)
5393
- throw new FeeCapTooHighError({ maxFeePerGas });
5394
- if (maxPriorityFeePerGas &&
5395
- maxFeePerGas &&
5396
- maxPriorityFeePerGas > maxFeePerGas)
5397
- throw new TipAboveFeeCapError({ maxFeePerGas, maxPriorityFeePerGas });
5398
- }
5399
- function assertTransactionEIP2930(transaction) {
5400
- const { chainId, maxPriorityFeePerGas, gasPrice, maxFeePerGas, to } = transaction;
5401
- if (chainId <= 0)
5402
- throw new InvalidChainIdError({ chainId });
5403
- if (to && !isAddress(to))
5404
- throw new InvalidAddressError({ address: to });
5405
- if (maxPriorityFeePerGas || maxFeePerGas)
5406
- throw new BaseError('`maxFeePerGas`/`maxPriorityFeePerGas` is not a valid EIP-2930 Transaction attribute.');
5407
- if (gasPrice && gasPrice > 2n ** 256n - 1n)
5408
- throw new FeeCapTooHighError({ maxFeePerGas: gasPrice });
5450
+ // 3. Concatenate static and dynamic parts.
5451
+ return concat([...staticParams, ...dynamicParams]);
5409
5452
  }
5410
- function assertTransactionLegacy(transaction) {
5411
- const { chainId, maxPriorityFeePerGas, gasPrice, maxFeePerGas, to, accessList, } = transaction;
5412
- if (to && !isAddress(to))
5413
- throw new InvalidAddressError({ address: to });
5414
- if (typeof chainId !== 'undefined' && chainId <= 0)
5415
- throw new InvalidChainIdError({ chainId });
5416
- if (maxPriorityFeePerGas || maxFeePerGas)
5417
- throw new BaseError('`maxFeePerGas`/`maxPriorityFeePerGas` is not a valid Legacy Transaction attribute.');
5418
- if (gasPrice && gasPrice > 2n ** 256n - 1n)
5419
- throw new FeeCapTooHighError({ maxFeePerGas: gasPrice });
5420
- if (accessList)
5421
- throw new BaseError('`accessList` is not a valid Legacy Transaction attribute.');
5453
+ function encodeAddress(value) {
5454
+ if (!isAddress(value))
5455
+ throw new InvalidAddressError({ address: value });
5456
+ return { dynamic: false, encoded: padHex(value.toLowerCase()) };
5422
5457
  }
5423
-
5424
- /*
5425
- * Serialize an EIP-2930 access list
5426
- * @remarks
5427
- * Use to create a transaction serializer with support for EIP-2930 access lists
5428
- *
5429
- * @param accessList - Array of objects of address and arrays of Storage Keys
5430
- * @throws InvalidAddressError, InvalidStorageKeySizeError
5431
- * @returns Array of hex strings
5432
- */
5433
- function serializeAccessList(accessList) {
5434
- if (!accessList || accessList.length === 0)
5435
- return [];
5436
- const serializedAccessList = [];
5437
- for (let i = 0; i < accessList.length; i++) {
5438
- const { address, storageKeys } = accessList[i];
5439
- for (let j = 0; j < storageKeys.length; j++) {
5440
- if (storageKeys[j].length - 2 !== 64) {
5441
- throw new InvalidStorageKeySizeError({ storageKey: storageKeys[j] });
5442
- }
5443
- }
5444
- if (!isAddress(address, { strict: false })) {
5445
- throw new InvalidAddressError({ address });
5458
+ function encodeArray(value, { length, param, }) {
5459
+ const dynamic = length === null;
5460
+ if (!Array.isArray(value))
5461
+ throw new InvalidArrayError(value);
5462
+ if (!dynamic && value.length !== length)
5463
+ throw new AbiEncodingArrayLengthMismatchError({
5464
+ expectedLength: length,
5465
+ givenLength: value.length,
5466
+ type: `${param.type}[${length}]`,
5467
+ });
5468
+ let dynamicChild = false;
5469
+ const preparedParams = [];
5470
+ for (let i = 0; i < value.length; i++) {
5471
+ const preparedParam = prepareParam({ param, value: value[i] });
5472
+ if (preparedParam.dynamic)
5473
+ dynamicChild = true;
5474
+ preparedParams.push(preparedParam);
5475
+ }
5476
+ if (dynamic || dynamicChild) {
5477
+ const data = encodeParams(preparedParams);
5478
+ if (dynamic) {
5479
+ const length = numberToHex(preparedParams.length, { size: 32 });
5480
+ return {
5481
+ dynamic: true,
5482
+ encoded: preparedParams.length > 0 ? concat([length, data]) : length,
5483
+ };
5446
5484
  }
5447
- serializedAccessList.push([address, storageKeys]);
5485
+ if (dynamicChild)
5486
+ return { dynamic: true, encoded: data };
5448
5487
  }
5449
- return serializedAccessList;
5488
+ return {
5489
+ dynamic: false,
5490
+ encoded: concat(preparedParams.map(({ encoded }) => encoded)),
5491
+ };
5450
5492
  }
5451
-
5452
- function toRlp(bytes, to = 'hex') {
5453
- const encodable = getEncodable(bytes);
5454
- const cursor = createCursor(new Uint8Array(encodable.length));
5455
- encodable.encode(cursor);
5456
- if (to === 'hex')
5457
- return bytesToHex(cursor.bytes);
5458
- return cursor.bytes;
5493
+ function encodeBytes(value, { param }) {
5494
+ const [, paramSize] = param.type.split('bytes');
5495
+ const bytesSize = size(value);
5496
+ if (!paramSize) {
5497
+ let value_ = value;
5498
+ // If the size is not divisible by 32 bytes, pad the end
5499
+ // with empty bytes to the ceiling 32 bytes.
5500
+ if (bytesSize % 32 !== 0)
5501
+ value_ = padHex(value_, {
5502
+ dir: 'right',
5503
+ size: Math.ceil((value.length - 2) / 2 / 32) * 32,
5504
+ });
5505
+ return {
5506
+ dynamic: true,
5507
+ encoded: concat([padHex(numberToHex(bytesSize, { size: 32 })), value_]),
5508
+ };
5509
+ }
5510
+ if (bytesSize !== Number.parseInt(paramSize))
5511
+ throw new AbiEncodingBytesSizeMismatchError({
5512
+ expectedSize: Number.parseInt(paramSize),
5513
+ value,
5514
+ });
5515
+ return { dynamic: false, encoded: padHex(value, { dir: 'right' }) };
5459
5516
  }
5460
- function getEncodable(bytes) {
5461
- if (Array.isArray(bytes))
5462
- return getEncodableList(bytes.map((x) => getEncodable(x)));
5463
- return getEncodableBytes(bytes);
5517
+ function encodeBool(value) {
5518
+ if (typeof value !== 'boolean')
5519
+ throw new BaseError(`Invalid boolean value: "${value}" (type: ${typeof value}). Expected: \`true\` or \`false\`.`);
5520
+ return { dynamic: false, encoded: padHex(boolToHex(value)) };
5464
5521
  }
5465
- function getEncodableList(list) {
5466
- const bodyLength = list.reduce((acc, x) => acc + x.length, 0);
5467
- const sizeOfBodyLength = getSizeOfLength(bodyLength);
5468
- const length = (() => {
5469
- if (bodyLength <= 55)
5470
- return 1 + bodyLength;
5471
- return 1 + sizeOfBodyLength + bodyLength;
5472
- })();
5522
+ function encodeNumber(value, { signed }) {
5473
5523
  return {
5474
- length,
5475
- encode(cursor) {
5476
- if (bodyLength <= 55) {
5477
- cursor.pushByte(0xc0 + bodyLength);
5478
- }
5479
- else {
5480
- cursor.pushByte(0xc0 + 55 + sizeOfBodyLength);
5481
- if (sizeOfBodyLength === 1)
5482
- cursor.pushUint8(bodyLength);
5483
- else if (sizeOfBodyLength === 2)
5484
- cursor.pushUint16(bodyLength);
5485
- else if (sizeOfBodyLength === 3)
5486
- cursor.pushUint24(bodyLength);
5487
- else
5488
- cursor.pushUint32(bodyLength);
5489
- }
5490
- for (const { encode } of list) {
5491
- encode(cursor);
5492
- }
5493
- },
5524
+ dynamic: false,
5525
+ encoded: numberToHex(value, {
5526
+ size: 32,
5527
+ signed,
5528
+ }),
5494
5529
  };
5495
5530
  }
5496
- function getEncodableBytes(bytesOrHex) {
5497
- const bytes = typeof bytesOrHex === 'string' ? hexToBytes(bytesOrHex) : bytesOrHex;
5498
- const sizeOfBytesLength = getSizeOfLength(bytes.length);
5499
- const length = (() => {
5500
- if (bytes.length === 1 && bytes[0] < 0x80)
5501
- return 1;
5502
- if (bytes.length <= 55)
5503
- return 1 + bytes.length;
5504
- return 1 + sizeOfBytesLength + bytes.length;
5505
- })();
5531
+ function encodeString(value) {
5532
+ const hexValue = stringToHex(value);
5533
+ const partsLength = Math.ceil(size(hexValue) / 32);
5534
+ const parts = [];
5535
+ for (let i = 0; i < partsLength; i++) {
5536
+ parts.push(padHex(slice(hexValue, i * 32, (i + 1) * 32), {
5537
+ dir: 'right',
5538
+ }));
5539
+ }
5506
5540
  return {
5507
- length,
5508
- encode(cursor) {
5509
- if (bytes.length === 1 && bytes[0] < 0x80) {
5510
- cursor.pushBytes(bytes);
5511
- }
5512
- else if (bytes.length <= 55) {
5513
- cursor.pushByte(0x80 + bytes.length);
5514
- cursor.pushBytes(bytes);
5515
- }
5516
- else {
5517
- cursor.pushByte(0x80 + 55 + sizeOfBytesLength);
5518
- if (sizeOfBytesLength === 1)
5519
- cursor.pushUint8(bytes.length);
5520
- else if (sizeOfBytesLength === 2)
5521
- cursor.pushUint16(bytes.length);
5522
- else if (sizeOfBytesLength === 3)
5523
- cursor.pushUint24(bytes.length);
5524
- else
5525
- cursor.pushUint32(bytes.length);
5526
- cursor.pushBytes(bytes);
5527
- }
5528
- },
5529
- };
5541
+ dynamic: true,
5542
+ encoded: concat([
5543
+ padHex(numberToHex(size(hexValue), { size: 32 })),
5544
+ ...parts,
5545
+ ]),
5546
+ };
5530
5547
  }
5531
- function getSizeOfLength(length) {
5532
- if (length < 2 ** 8)
5533
- return 1;
5534
- if (length < 2 ** 16)
5535
- return 2;
5536
- if (length < 2 ** 24)
5537
- return 3;
5538
- if (length < 2 ** 32)
5539
- return 4;
5540
- throw new BaseError('Length is too large.');
5548
+ function encodeTuple(value, { param }) {
5549
+ let dynamic = false;
5550
+ const preparedParams = [];
5551
+ for (let i = 0; i < param.components.length; i++) {
5552
+ const param_ = param.components[i];
5553
+ const index = Array.isArray(value) ? i : param_.name;
5554
+ const preparedParam = prepareParam({
5555
+ param: param_,
5556
+ value: value[index],
5557
+ });
5558
+ preparedParams.push(preparedParam);
5559
+ if (preparedParam.dynamic)
5560
+ dynamic = true;
5561
+ }
5562
+ return {
5563
+ dynamic,
5564
+ encoded: dynamic
5565
+ ? encodeParams(preparedParams)
5566
+ : concat(preparedParams.map(({ encoded }) => encoded)),
5567
+ };
5568
+ }
5569
+ function getArrayComponents(type) {
5570
+ const matches = type.match(/^(.*)\[(\d+)?\]$/);
5571
+ return matches
5572
+ ? // Return `null` if the array is dynamic.
5573
+ [matches[2] ? Number(matches[2]) : null, matches[1]]
5574
+ : undefined;
5541
5575
  }
5542
5576
 
5543
- function serializeTransaction(transaction, signature) {
5544
- const type = getTransactionType(transaction);
5545
- if (type === 'eip1559')
5546
- return serializeTransactionEIP1559(transaction, signature);
5547
- if (type === 'eip2930')
5548
- return serializeTransactionEIP2930(transaction, signature);
5549
- if (type === 'eip4844')
5550
- return serializeTransactionEIP4844(transaction, signature);
5551
- return serializeTransactionLegacy(transaction, signature);
5577
+ // `bytes<M>`: binary type of `M` bytes, `0 < M <= 32`
5578
+ // https://regexr.com/6va55
5579
+ const bytesRegex = /^bytes([1-9]|1[0-9]|2[0-9]|3[0-2])?$/;
5580
+ // `(u)int<M>`: (un)signed integer type of `M` bits, `0 < M <= 256`, `M % 8 == 0`
5581
+ // https://regexr.com/6v8hp
5582
+ const integerRegex = /^(u?int)(8|16|24|32|40|48|56|64|72|80|88|96|104|112|120|128|136|144|152|160|168|176|184|192|200|208|216|224|232|240|248|256)?$/;
5583
+
5584
+ function validateTypedData(parameters) {
5585
+ const { domain, message, primaryType, types } = parameters;
5586
+ const validateData = (struct, data) => {
5587
+ for (const param of struct) {
5588
+ const { name, type } = param;
5589
+ const value = data[name];
5590
+ const integerMatch = type.match(integerRegex);
5591
+ if (integerMatch &&
5592
+ (typeof value === 'number' || typeof value === 'bigint')) {
5593
+ const [_type, base, size_] = integerMatch;
5594
+ // If number cannot be cast to a sized hex value, it is out of range
5595
+ // and will throw.
5596
+ numberToHex(value, {
5597
+ signed: base === 'int',
5598
+ size: Number.parseInt(size_) / 8,
5599
+ });
5600
+ }
5601
+ if (type === 'address' && typeof value === 'string' && !isAddress(value))
5602
+ throw new InvalidAddressError({ address: value });
5603
+ const bytesMatch = type.match(bytesRegex);
5604
+ if (bytesMatch) {
5605
+ const [_type, size_] = bytesMatch;
5606
+ if (size_ && size(value) !== Number.parseInt(size_))
5607
+ throw new BytesSizeMismatchError({
5608
+ expectedSize: Number.parseInt(size_),
5609
+ givenSize: size(value),
5610
+ });
5611
+ }
5612
+ const struct = types[type];
5613
+ if (struct)
5614
+ validateData(struct, value);
5615
+ }
5616
+ };
5617
+ // Validate domain types.
5618
+ if (types.EIP712Domain && domain)
5619
+ validateData(types.EIP712Domain, domain);
5620
+ // Validate message types.
5621
+ if (primaryType !== 'EIP712Domain')
5622
+ validateData(types[primaryType], message);
5552
5623
  }
5553
- function serializeTransactionEIP4844(transaction, signature) {
5554
- const { chainId, gas, nonce, to, value, maxFeePerBlobGas, maxFeePerGas, maxPriorityFeePerGas, accessList, data, } = transaction;
5555
- assertTransactionEIP4844(transaction);
5556
- let blobVersionedHashes = transaction.blobVersionedHashes;
5557
- let sidecars = transaction.sidecars;
5558
- // If `blobs` are passed, we will need to compute the KZG commitments & proofs.
5559
- if (transaction.blobs &&
5560
- (typeof blobVersionedHashes === 'undefined' ||
5561
- typeof sidecars === 'undefined')) {
5562
- const blobs = (typeof transaction.blobs[0] === 'string'
5563
- ? transaction.blobs
5564
- : transaction.blobs.map((x) => bytesToHex(x)));
5565
- const kzg = transaction.kzg;
5566
- const commitments = blobsToCommitments({
5567
- blobs,
5568
- kzg,
5624
+ function getTypesForEIP712Domain({ domain, }) {
5625
+ return [
5626
+ typeof domain?.name === 'string' && { name: 'name', type: 'string' },
5627
+ domain?.version && { name: 'version', type: 'string' },
5628
+ typeof domain?.chainId === 'number' && {
5629
+ name: 'chainId',
5630
+ type: 'uint256',
5631
+ },
5632
+ domain?.verifyingContract && {
5633
+ name: 'verifyingContract',
5634
+ type: 'address',
5635
+ },
5636
+ domain?.salt && { name: 'salt', type: 'bytes32' },
5637
+ ].filter(Boolean);
5638
+ }
5639
+
5640
+ // Implementation forked and adapted from https://github.com/MetaMask/eth-sig-util/blob/main/src/sign-typed-data.ts
5641
+ function hashTypedData(parameters) {
5642
+ const { domain = {}, message, primaryType, } = parameters;
5643
+ const types = {
5644
+ EIP712Domain: getTypesForEIP712Domain({ domain }),
5645
+ ...parameters.types,
5646
+ };
5647
+ // Need to do a runtime validation check on addresses, byte ranges, integer ranges, etc
5648
+ // as we can't statically check this with TypeScript.
5649
+ validateTypedData({
5650
+ domain,
5651
+ message,
5652
+ primaryType,
5653
+ types,
5654
+ });
5655
+ const parts = ['0x1901'];
5656
+ if (domain)
5657
+ parts.push(hashDomain({
5658
+ domain,
5659
+ types: types,
5660
+ }));
5661
+ if (primaryType !== 'EIP712Domain')
5662
+ parts.push(hashStruct({
5663
+ data: message,
5664
+ primaryType,
5665
+ types: types,
5666
+ }));
5667
+ return keccak256(concat(parts));
5668
+ }
5669
+ function hashDomain({ domain, types, }) {
5670
+ return hashStruct({
5671
+ data: domain,
5672
+ primaryType: 'EIP712Domain',
5673
+ types,
5674
+ });
5675
+ }
5676
+ function hashStruct({ data, primaryType, types, }) {
5677
+ const encoded = encodeData({
5678
+ data,
5679
+ primaryType,
5680
+ types,
5681
+ });
5682
+ return keccak256(encoded);
5683
+ }
5684
+ function encodeData({ data, primaryType, types, }) {
5685
+ const encodedTypes = [{ type: 'bytes32' }];
5686
+ const encodedValues = [hashType({ primaryType, types })];
5687
+ for (const field of types[primaryType]) {
5688
+ const [type, value] = encodeField({
5689
+ types,
5690
+ name: field.name,
5691
+ type: field.type,
5692
+ value: data[field.name],
5569
5693
  });
5570
- if (typeof blobVersionedHashes === 'undefined')
5571
- blobVersionedHashes = commitmentsToVersionedHashes({
5572
- commitments,
5573
- });
5574
- if (typeof sidecars === 'undefined') {
5575
- const proofs = blobsToProofs({ blobs, commitments, kzg });
5576
- sidecars = toBlobSidecars({ blobs, commitments, proofs });
5577
- }
5694
+ encodedTypes.push(type);
5695
+ encodedValues.push(value);
5578
5696
  }
5579
- const serializedAccessList = serializeAccessList(accessList);
5580
- const serializedTransaction = [
5581
- toHex(chainId),
5582
- nonce ? toHex(nonce) : '0x',
5583
- maxPriorityFeePerGas ? toHex(maxPriorityFeePerGas) : '0x',
5584
- maxFeePerGas ? toHex(maxFeePerGas) : '0x',
5585
- gas ? toHex(gas) : '0x',
5586
- to ?? '0x',
5587
- value ? toHex(value) : '0x',
5588
- data ?? '0x',
5589
- serializedAccessList,
5590
- maxFeePerBlobGas ? toHex(maxFeePerBlobGas) : '0x',
5591
- blobVersionedHashes ?? [],
5592
- ...toYParitySignatureArray(transaction, signature),
5593
- ];
5594
- const blobs = [];
5595
- const commitments = [];
5596
- const proofs = [];
5597
- if (sidecars)
5598
- for (let i = 0; i < sidecars.length; i++) {
5599
- const { blob, commitment, proof } = sidecars[i];
5600
- blobs.push(blob);
5601
- commitments.push(commitment);
5602
- proofs.push(proof);
5603
- }
5604
- return concatHex([
5605
- '0x03',
5606
- sidecars
5607
- ? // If sidecars are enabled, envelope turns into a "wrapper":
5608
- toRlp([serializedTransaction, blobs, commitments, proofs])
5609
- : // If sidecars are disabled, standard envelope is used:
5610
- toRlp(serializedTransaction),
5611
- ]);
5697
+ return encodeAbiParameters(encodedTypes, encodedValues);
5612
5698
  }
5613
- function serializeTransactionEIP1559(transaction, signature) {
5614
- const { chainId, gas, nonce, to, value, maxFeePerGas, maxPriorityFeePerGas, accessList, data, } = transaction;
5615
- assertTransactionEIP1559(transaction);
5616
- const serializedAccessList = serializeAccessList(accessList);
5617
- const serializedTransaction = [
5618
- toHex(chainId),
5619
- nonce ? toHex(nonce) : '0x',
5620
- maxPriorityFeePerGas ? toHex(maxPriorityFeePerGas) : '0x',
5621
- maxFeePerGas ? toHex(maxFeePerGas) : '0x',
5622
- gas ? toHex(gas) : '0x',
5623
- to ?? '0x',
5624
- value ? toHex(value) : '0x',
5625
- data ?? '0x',
5626
- serializedAccessList,
5627
- ...toYParitySignatureArray(transaction, signature),
5628
- ];
5629
- return concatHex([
5630
- '0x02',
5631
- toRlp(serializedTransaction),
5632
- ]);
5699
+ function hashType({ primaryType, types, }) {
5700
+ const encodedHashType = toHex(encodeType({ primaryType, types }));
5701
+ return keccak256(encodedHashType);
5633
5702
  }
5634
- function serializeTransactionEIP2930(transaction, signature) {
5635
- const { chainId, gas, data, nonce, to, value, accessList, gasPrice } = transaction;
5636
- assertTransactionEIP2930(transaction);
5637
- const serializedAccessList = serializeAccessList(accessList);
5638
- const serializedTransaction = [
5639
- toHex(chainId),
5640
- nonce ? toHex(nonce) : '0x',
5641
- gasPrice ? toHex(gasPrice) : '0x',
5642
- gas ? toHex(gas) : '0x',
5643
- to ?? '0x',
5644
- value ? toHex(value) : '0x',
5645
- data ?? '0x',
5646
- serializedAccessList,
5647
- ...toYParitySignatureArray(transaction, signature),
5648
- ];
5649
- return concatHex([
5650
- '0x01',
5651
- toRlp(serializedTransaction),
5652
- ]);
5703
+ function encodeType({ primaryType, types, }) {
5704
+ let result = '';
5705
+ const unsortedDeps = findTypeDependencies({ primaryType, types });
5706
+ unsortedDeps.delete(primaryType);
5707
+ const deps = [primaryType, ...Array.from(unsortedDeps).sort()];
5708
+ for (const type of deps) {
5709
+ result += `${type}(${types[type]
5710
+ .map(({ name, type: t }) => `${t} ${name}`)
5711
+ .join(',')})`;
5712
+ }
5713
+ return result;
5653
5714
  }
5654
- function serializeTransactionLegacy(transaction, signature) {
5655
- const { chainId = 0, gas, data, nonce, to, value, gasPrice } = transaction;
5656
- assertTransactionLegacy(transaction);
5657
- let serializedTransaction = [
5658
- nonce ? toHex(nonce) : '0x',
5659
- gasPrice ? toHex(gasPrice) : '0x',
5660
- gas ? toHex(gas) : '0x',
5661
- to ?? '0x',
5662
- value ? toHex(value) : '0x',
5663
- data ?? '0x',
5664
- ];
5665
- if (signature) {
5666
- const v = (() => {
5667
- // EIP-155 (inferred chainId)
5668
- if (signature.v >= 35n) {
5669
- const inferredChainId = (signature.v - 35n) / 2n;
5670
- if (inferredChainId > 0)
5671
- return signature.v;
5672
- return 27n + (signature.v === 35n ? 0n : 1n);
5673
- }
5674
- // EIP-155 (explicit chainId)
5675
- if (chainId > 0)
5676
- return BigInt(chainId * 2) + BigInt(35n + signature.v - 27n);
5677
- // Pre-EIP-155 (no chainId)
5678
- const v = 27n + (signature.v === 27n ? 0n : 1n);
5679
- if (signature.v !== v)
5680
- throw new InvalidLegacyVError({ v: signature.v });
5681
- return v;
5682
- })();
5683
- serializedTransaction = [
5684
- ...serializedTransaction,
5685
- toHex(v),
5686
- signature.r,
5687
- signature.s,
5715
+ function findTypeDependencies({ primaryType: primaryType_, types, }, results = new Set()) {
5716
+ const match = primaryType_.match(/^\w*/u);
5717
+ const primaryType = match?.[0];
5718
+ if (results.has(primaryType) || types[primaryType] === undefined) {
5719
+ return results;
5720
+ }
5721
+ results.add(primaryType);
5722
+ for (const field of types[primaryType]) {
5723
+ findTypeDependencies({ primaryType: field.type, types }, results);
5724
+ }
5725
+ return results;
5726
+ }
5727
+ function encodeField({ types, name, type, value, }) {
5728
+ if (types[type] !== undefined) {
5729
+ return [
5730
+ { type: 'bytes32' },
5731
+ keccak256(encodeData({ data: value, primaryType: type, types })),
5688
5732
  ];
5689
5733
  }
5690
- else if (chainId > 0) {
5691
- serializedTransaction = [
5692
- ...serializedTransaction,
5693
- toHex(chainId),
5694
- '0x',
5695
- '0x',
5734
+ if (type === 'bytes') {
5735
+ const prepend = value.length % 2 ? '0' : '';
5736
+ value = `0x${prepend + value.slice(2)}`;
5737
+ return [{ type: 'bytes32' }, keccak256(value)];
5738
+ }
5739
+ if (type === 'string')
5740
+ return [{ type: 'bytes32' }, keccak256(toHex(value))];
5741
+ if (type.lastIndexOf(']') === type.length - 1) {
5742
+ const parsedType = type.slice(0, type.lastIndexOf('['));
5743
+ const typeValuePairs = value.map((item) => encodeField({
5744
+ name,
5745
+ type: parsedType,
5746
+ types,
5747
+ value: item,
5748
+ }));
5749
+ return [
5750
+ { type: 'bytes32' },
5751
+ keccak256(encodeAbiParameters(typeValuePairs.map(([t]) => t), typeValuePairs.map(([, v]) => v))),
5696
5752
  ];
5697
5753
  }
5698
- return toRlp(serializedTransaction);
5699
- }
5700
- function toYParitySignatureArray(transaction, signature) {
5701
- const { r, s, v, yParity } = signature ?? transaction;
5702
- if (typeof r === 'undefined')
5703
- return [];
5704
- if (typeof s === 'undefined')
5705
- return [];
5706
- if (typeof v === 'undefined' && typeof yParity === 'undefined')
5707
- return [];
5708
- const yParity_ = (() => {
5709
- if (typeof yParity === 'number')
5710
- return yParity ? toHex(1) : '0x';
5711
- if (v === 0n)
5712
- return '0x';
5713
- if (v === 1n)
5714
- return toHex(1);
5715
- return v === 27n ? '0x' : toHex(1);
5716
- })();
5717
- return [yParity_, trim(r), trim(s)];
5718
- }
5719
-
5720
- async function signTransaction(parameters) {
5721
- const { privateKey, transaction, serializer = serializeTransaction, } = parameters;
5722
- const signableTransaction = (() => {
5723
- // For EIP-4844 Transactions, we want to sign the transaction payload body (tx_payload_body) without the sidecars (ie. without the network wrapper).
5724
- // See: https://github.com/ethereum/EIPs/blob/e00f4daa66bd56e2dbd5f1d36d09fd613811a48b/EIPS/eip-4844.md#networking
5725
- if (transaction.type === 'eip4844')
5726
- return {
5727
- ...transaction,
5728
- sidecars: false,
5729
- };
5730
- return transaction;
5731
- })();
5732
- const signature = await sign({
5733
- hash: keccak256(serializer(signableTransaction)),
5734
- privateKey,
5735
- });
5736
- return serializer(transaction, signature);
5754
+ return [{ type }, value];
5737
5755
  }
5738
5756
 
5739
5757
  /**
@@ -5748,7 +5766,7 @@ async function signTypedData(parameters) {
5748
5766
  hash: hashTypedData(typedData),
5749
5767
  privateKey,
5750
5768
  });
5751
- return signatureToHex(signature);
5769
+ return serializeSignature(signature);
5752
5770
  }
5753
5771
 
5754
5772
  /**
@@ -5756,11 +5774,13 @@ async function signTypedData(parameters) {
5756
5774
  *
5757
5775
  * @returns A Private Key Account.
5758
5776
  */
5759
- function privateKeyToAccount(privateKey) {
5777
+ function privateKeyToAccount(privateKey, options = {}) {
5778
+ const { nonceManager } = options;
5760
5779
  const publicKey = toHex(secp256k1.getPublicKey(privateKey.slice(2), false));
5761
5780
  const address = publicKeyToAddress(publicKey);
5762
5781
  const account = toAccount({
5763
5782
  address,
5783
+ nonceManager,
5764
5784
  async signMessage({ message }) {
5765
5785
  return signMessage({ message, privateKey });
5766
5786
  },
@@ -5783,9 +5803,9 @@ function privateKeyToAccount(privateKey) {
5783
5803
  *
5784
5804
  * @returns A HD Account.
5785
5805
  */
5786
- function hdKeyToAccount(hdKey_, { accountIndex = 0, addressIndex = 0, changeIndex = 0, path } = {}) {
5806
+ function hdKeyToAccount(hdKey_, { accountIndex = 0, addressIndex = 0, changeIndex = 0, path, ...options } = {}) {
5787
5807
  const hdKey = hdKey_.derive(path || `m/44'/60'/${accountIndex}'/${changeIndex}/${addressIndex}`);
5788
- const account = privateKeyToAccount(toHex(hdKey.privateKey));
5808
+ const account = privateKeyToAccount(toHex(hdKey.privateKey), options);
5789
5809
  return {
5790
5810
  ...account,
5791
5811
  getHdKey: () => hdKey,