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