account.py 48 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150
  1. from collections.abc import (
  2. Mapping,
  3. )
  4. from copy import (
  5. copy,
  6. )
  7. import json
  8. import os
  9. from typing import (
  10. Any,
  11. Dict,
  12. Optional,
  13. Tuple,
  14. TypeVar,
  15. Union,
  16. cast,
  17. )
  18. from eth_keyfile import (
  19. create_keyfile_json,
  20. decode_keyfile_json,
  21. )
  22. from eth_keyfile.keyfile import (
  23. KDFType,
  24. )
  25. from eth_keys import (
  26. KeyAPI,
  27. keys,
  28. )
  29. from eth_keys.backends import (
  30. CoinCurveECCBackend,
  31. NativeECCBackend,
  32. )
  33. from eth_keys.datatypes import (
  34. PrivateKey,
  35. )
  36. from eth_keys.exceptions import (
  37. ValidationError,
  38. )
  39. from eth_typing import (
  40. ChecksumAddress,
  41. Hash32,
  42. HexStr,
  43. )
  44. from eth_utils import (
  45. to_canonical_address,
  46. )
  47. from eth_utils.curried import (
  48. combomethod,
  49. hexstr_if_str,
  50. is_dict,
  51. keccak,
  52. text_if_str,
  53. to_bytes,
  54. to_int,
  55. )
  56. from eth_utils.toolz import (
  57. dissoc,
  58. )
  59. from hexbytes import (
  60. HexBytes,
  61. )
  62. from eth_account._utils.legacy_transactions import (
  63. Transaction,
  64. vrs_from,
  65. )
  66. from eth_account._utils.signing import (
  67. hash_of_signed_transaction,
  68. sign_message_hash,
  69. sign_transaction_dict,
  70. to_standard_signature_bytes,
  71. to_standard_v,
  72. )
  73. from eth_account._utils.validation import (
  74. validate_and_set_default_kdf,
  75. )
  76. from eth_account.account_local_actions import (
  77. AccountLocalActions,
  78. )
  79. from eth_account.datastructures import (
  80. SignedMessage,
  81. SignedSetCodeAuthorization,
  82. SignedTransaction,
  83. )
  84. from eth_account.hdaccount import (
  85. ETHEREUM_DEFAULT_PATH,
  86. generate_mnemonic,
  87. key_from_seed,
  88. seed_from_mnemonic,
  89. )
  90. from eth_account.messages import (
  91. SignableMessage,
  92. _hash_eip191_message,
  93. encode_typed_data,
  94. )
  95. from eth_account.signers.local import (
  96. LocalAccount,
  97. )
  98. from eth_account.typed_transactions import (
  99. TypedTransaction,
  100. )
  101. from eth_account.typed_transactions.set_code_transaction import (
  102. Authorization,
  103. )
  104. from eth_account.types import (
  105. AuthorizationDict,
  106. Blobs,
  107. Language,
  108. PrivateKeyType,
  109. TransactionDictType,
  110. )
  111. VRS = TypeVar("VRS", bytes, HexStr, int)
  112. class Account(AccountLocalActions):
  113. """
  114. The primary entry point for working with Ethereum private keys.
  115. It does **not** require a connection to an Ethereum node.
  116. """
  117. _keys = keys
  118. # Enable unaudited features (off by default)
  119. _use_unaudited_hdwallet_features = False
  120. _default_kdf: KDFType = validate_and_set_default_kdf()
  121. @classmethod
  122. def enable_unaudited_hdwallet_features(cls) -> None:
  123. """
  124. Use this flag to enable unaudited HD Wallet features.
  125. """
  126. cls._use_unaudited_hdwallet_features = True
  127. @combomethod
  128. def create(self, extra_entropy: Union[str, bytes, int] = "") -> LocalAccount:
  129. r"""
  130. Creates a new private key, and returns it as a
  131. :class:`~eth_account.local.LocalAccount`.
  132. :param extra_entropy: Add extra randomness to whatever randomness your OS
  133. can provide
  134. :type extra_entropy: str or bytes or int
  135. :returns: an object with private key and convenience methods
  136. .. code-block:: python
  137. >>> from eth_account import Account
  138. >>> acct = Account.create('KEYSMASH FJAFJKLDSKF7JKFDJ 1530')
  139. >>> acct.address
  140. '0x5ce9454909639D2D17A3F753ce7d93fa0b9aB12E'
  141. >>> acct.key
  142. HexBytes('0x8676e9a8c86c8921e922e61e0bb6e9e9689aad4c99082620610b00140e5f21b8')
  143. # These methods are also available: sign_message(), sign_transaction(),
  144. # encrypt().
  145. # They correspond to the same-named methods in Account.*
  146. # but without the private key argument
  147. """
  148. extra_key_bytes = text_if_str(to_bytes, extra_entropy)
  149. key_bytes = keccak(os.urandom(32) + extra_key_bytes)
  150. return cast(LocalAccount, self.from_key(key_bytes))
  151. @staticmethod
  152. def decrypt(keyfile_json: Union[str, Dict[str, Any]], password: str) -> HexBytes:
  153. """
  154. Decrypts a private key.
  155. The key may have been encrypted using an Ethereum client or
  156. :meth:`~Account.encrypt`.
  157. :param keyfile_json: The encrypted key
  158. :type keyfile_json: dict or str
  159. :param str password: The password that was used to encrypt the key
  160. :returns: the raw private key
  161. :rtype: ~hexbytes.main.HexBytes
  162. .. doctest:: python
  163. >>> encrypted = {
  164. ... 'address': '5ce9454909639D2D17A3F753ce7d93fa0b9aB12E',
  165. ... 'crypto': {'cipher': 'aes-128-ctr',
  166. ... 'cipherparams': {'iv': '482ef54775b0cc59f25717711286f5c8'},
  167. ... 'ciphertext': 'cb636716a9fd46adbb31832d964df2082536edd5399a3393327dc89b0193a2be',
  168. ... 'kdf': 'scrypt',
  169. ... 'kdfparams': {},
  170. ... 'kdfparams': {'dklen': 32,
  171. ... 'n': 262144,
  172. ... 'p': 8,
  173. ... 'r': 1,
  174. ... 'salt': 'd3c9a9945000fcb6c9df0f854266d573'},
  175. ... 'mac': '4f626ec5e7fea391b2229348a65bfef532c2a4e8372c0a6a814505a350a7689d'},
  176. ... 'id': 'b812f3f9-78cc-462a-9e89-74418aa27cb0',
  177. ... 'version': 3}
  178. >>> Account.decrypt(encrypted, 'password')
  179. HexBytes('0xb25c7db31feed9122727bf0939dc769a96564b2de4c4726d035b36ecf1e5b364')
  180. """ # noqa: E501
  181. if isinstance(keyfile_json, str):
  182. keyfile = json.loads(keyfile_json)
  183. elif is_dict(keyfile_json):
  184. keyfile = keyfile_json
  185. else:
  186. raise TypeError(
  187. "The keyfile should be supplied as a JSON string, or a dictionary."
  188. )
  189. password_bytes = text_if_str(to_bytes, password)
  190. # type ignored because eth_keyfile appears to be using the wrong type for
  191. # the password arg.
  192. # once fixed there, this should error and can be removed
  193. return HexBytes(decode_keyfile_json(keyfile, password_bytes)) # type: ignore[arg-type] # noqa: E501
  194. @classmethod
  195. def encrypt(
  196. cls,
  197. private_key: PrivateKeyType,
  198. password: str,
  199. kdf: Optional[KDFType] = None,
  200. iterations: Optional[int] = None,
  201. ) -> Dict[str, Any]:
  202. """
  203. Creates a dictionary with an encrypted version of your private key.
  204. To import this keyfile into Ethereum clients like geth and parity:
  205. encode this dictionary with :func:`json.dumps` and save it to disk where your
  206. client keeps key files.
  207. :param private_key: The raw private key
  208. :type private_key: hex str, bytes, int or :class:`eth_keys.datatypes.PrivateKey`
  209. :param str password: The password which you will need to unlock the account
  210. in your client
  211. :param str kdf: The key derivation function to use when encrypting your
  212. private key
  213. :param int iterations: The work factor for the key derivation function
  214. :returns: The data to use in your encrypted file
  215. :rtype: dict
  216. If kdf is not set, the default key derivation function falls back to the
  217. environment variable :envvar:`ETH_ACCOUNT_KDF`. If that is not set, then
  218. 'scrypt' will be used as the default.
  219. .. doctest:: python
  220. >>> from pprint import pprint
  221. >>> encrypted = Account.encrypt(
  222. ... 0xb25c7db31feed9122727bf0939dc769a96564b2de4c4726d035b36ecf1e5b364,
  223. ... 'password'
  224. ... )
  225. >>> pprint(encrypted)
  226. {'address': '5ce9454909639D2D17A3F753ce7d93fa0b9aB12E',
  227. 'crypto': {'cipher': 'aes-128-ctr',
  228. 'cipherparams': {'iv': '...'},
  229. 'ciphertext': '...',
  230. 'kdf': 'scrypt',
  231. 'kdfparams': {'dklen': 32,
  232. 'n': 262144,
  233. 'p': 1,
  234. 'r': 8,
  235. 'salt': '...'},
  236. 'mac': '...'},
  237. 'id': '...',
  238. 'version': 3}
  239. >>> with open('my-keyfile', 'w') as f: # doctest: +SKIP
  240. ... f.write(json.dumps(encrypted))
  241. """
  242. if isinstance(private_key, keys.PrivateKey):
  243. key_bytes = private_key.to_bytes()
  244. else:
  245. key_bytes = HexBytes(private_key)
  246. if kdf is None:
  247. kdf = cls._default_kdf
  248. password_bytes = text_if_str(to_bytes, password)
  249. assert len(key_bytes) == 32
  250. # type ignored because eth_keyfile appears to be using the wrong type for
  251. # the password arg.
  252. # once fixed there, this should error and can be removed
  253. return create_keyfile_json(
  254. key_bytes, password_bytes, kdf=kdf, iterations=iterations # type: ignore[arg-type] # noqa: E501
  255. )
  256. @combomethod
  257. def from_key(self, private_key: PrivateKeyType) -> LocalAccount:
  258. r"""
  259. Returns a convenient object for working with the given private key.
  260. :param private_key: The raw private key
  261. :type private_key: hex str, bytes, int or :class:`eth_keys.datatypes.PrivateKey`
  262. :return: object with methods for signing and encrypting
  263. :rtype: LocalAccount
  264. .. doctest:: python
  265. >>> acct = Account.from_key(
  266. ... 0xb25c7db31feed9122727bf0939dc769a96564b2de4c4726d035b36ecf1e5b364)
  267. >>> acct.address
  268. '0x5ce9454909639D2D17A3F753ce7d93fa0b9aB12E'
  269. >>> acct.key
  270. HexBytes('0xb25c7db31feed9122727bf0939dc769a96564b2de4c4726d035b36ecf1e5b364')
  271. # These methods are also available: sign_message(), sign_transaction(),
  272. # encrypt(). They correspond to the same-named methods in Account.*
  273. # but without the private key argument
  274. """
  275. key = self._parse_private_key(private_key)
  276. return LocalAccount(key, self)
  277. @combomethod
  278. def from_mnemonic(
  279. self,
  280. mnemonic: str,
  281. passphrase: str = "",
  282. account_path: str = ETHEREUM_DEFAULT_PATH,
  283. ) -> LocalAccount:
  284. """
  285. Generate an account from a mnemonic.
  286. .. CAUTION:: This feature is experimental, unaudited, and likely to change soon
  287. :param str mnemonic: space-separated list of BIP39 mnemonic seed words
  288. :param str passphrase: Optional passphrase used to encrypt the mnemonic
  289. :param str account_path: Specify an alternate HD path for deriving the seed
  290. using BIP32 HD wallet key derivation.
  291. :return: object with methods for signing and encrypting
  292. :rtype: LocalAccount
  293. .. doctest:: python
  294. >>> from eth_account import Account
  295. >>> Account.enable_unaudited_hdwallet_features()
  296. >>> acct = Account.from_mnemonic(
  297. ... "coral allow abandon recipe top tray caught video climb similar "
  298. ... "prepare bracket antenna rubber announce gauge volume "
  299. ... "hub hood burden skill immense add acid")
  300. >>> acct.address
  301. '0x9AdA5dAD14d925f4df1378409731a9B71Bc8569d'
  302. # These methods are also available: sign_message(), sign_transaction(),
  303. # encrypt(). They correspond to the same-named methods in Account.*
  304. # but without the private key argument
  305. Or, generate multiple accounts from a mnemonic.
  306. >>> from eth_account import Account
  307. >>> Account.enable_unaudited_hdwallet_features()
  308. >>> iterator = 0
  309. >>> for i in range(10):
  310. ... acct = Account.from_mnemonic(
  311. ... "health embark april buyer eternal leopard "
  312. ... "want before nominee head thing tackle",
  313. ... account_path=f"m/44'/60'/0'/0/{iterator}")
  314. ... iterator = iterator + 1
  315. ... acct.address
  316. '0x61Cc15522D06983Ac7aADe23f9d5433d38e78195'
  317. '0x1240460F6E370f28079E5F9B52f9DcB759F051b7'
  318. '0xd30dC9f996539826C646Eb48bb45F6ee1D1474af'
  319. '0x47e64beb58c9A469c5eD086aD231940676b44e7C'
  320. '0x6D39032ffEF9987988a069F52EFe4d95D0770555'
  321. '0x3836A6530D1889853b047799Ecd8827255072e77'
  322. '0xed5490dEfF8d8FfAe45cb4066C3daC7C6BFF6a22'
  323. '0xf04F9Ff322799253bcC6B12762AD127570a092c5'
  324. '0x900F7fa9fbe85BB25b6cdB94Da24D807f7feb213'
  325. '0xa248e118b0D19010387b1B768686cd9B473FA137'
  326. .. CAUTION:: For the love of Bob please do not use this mnemonic,
  327. it is for testing purposes only.
  328. """
  329. if not self._use_unaudited_hdwallet_features:
  330. raise AttributeError(
  331. "The use of the Mnemonic features of Account is disabled by "
  332. "default until its API stabilizes. To use these features, please "
  333. "enable them by running `Account.enable_unaudited_hdwallet_features()` "
  334. "and try again."
  335. )
  336. seed = seed_from_mnemonic(mnemonic, passphrase)
  337. private_key = key_from_seed(seed, account_path)
  338. key = self._parse_private_key(private_key)
  339. return LocalAccount(key, self)
  340. @combomethod
  341. def create_with_mnemonic(
  342. self,
  343. passphrase: str = "",
  344. num_words: int = 12,
  345. language: Union[Language, str] = Language.ENGLISH,
  346. account_path: str = ETHEREUM_DEFAULT_PATH,
  347. ) -> Tuple[LocalAccount, str]:
  348. r"""
  349. Create a new private key and related mnemonic.
  350. .. CAUTION:: This feature is experimental, unaudited, and likely to change soon
  351. Creates a new private key, and returns it as a
  352. :class:`~eth_account.local.LocalAccount`, alongside the mnemonic that can
  353. used to regenerate it using any BIP39-compatible wallet.
  354. :param str passphrase: Extra passphrase to encrypt the seed phrase
  355. :param int num_words: Number of words to use with seed phrase.
  356. Default is 12 words.
  357. Must be one of [12, 15, 18, 21, 24].
  358. :param (Language, str) language: Language to use for BIP39 mnemonic seed phrase.
  359. The use of a string is deprecated and will be
  360. removed in a future version.
  361. :param str account_path: Specify an alternate HD path for deriving the
  362. seed using BIP32 HD wallet key derivation.
  363. :returns: A tuple consisting of an object with private key and
  364. convenience methods, and the mnemonic seed phrase that can be
  365. used to restore the account.
  366. :rtype: (LocalAccount, str)
  367. .. doctest:: python
  368. >>> from eth_account import Account
  369. >>> Account.enable_unaudited_hdwallet_features()
  370. >>> acct, mnemonic = Account.create_with_mnemonic()
  371. >>> acct.address # doctest: +SKIP
  372. '0x5ce9454909639D2D17A3F753ce7d93fa0b9aB12E'
  373. >>> acct == Account.from_mnemonic(mnemonic)
  374. True
  375. # These methods are also available:
  376. # sign_message(), sign_transaction(), encrypt()
  377. # They correspond to the same-named methods in Account.*
  378. # but without the private key argument
  379. """
  380. if not self._use_unaudited_hdwallet_features:
  381. raise AttributeError(
  382. "The use of the Mnemonic features of Account is disabled by "
  383. "default until its API stabilizes. To use these features, please "
  384. "enable them by running `Account.enable_unaudited_hdwallet_features()` "
  385. "and try again."
  386. )
  387. mnemonic = generate_mnemonic(num_words, language)
  388. return self.from_mnemonic(mnemonic, passphrase, account_path), mnemonic
  389. @combomethod
  390. def recover_message(
  391. self,
  392. signable_message: SignableMessage,
  393. vrs: Optional[Tuple[VRS, VRS, VRS]] = None,
  394. signature: Optional[bytes] = None,
  395. ) -> ChecksumAddress:
  396. r"""
  397. Get the address of the account that signed the given message.
  398. You must specify exactly one of: vrs or signature
  399. :param signable_message: the message that was signed
  400. :param vrs: the three pieces generated by an elliptic curve signature
  401. :type vrs: tuple(v, r, s), each element is hex str, bytes or int
  402. :param signature: signature bytes concatenated as r+s+v
  403. :type signature: hex str or bytes or int
  404. :returns: address of signer, hex-encoded & checksummed
  405. :rtype: str
  406. .. doctest:: python
  407. >>> from eth_account.messages import encode_defunct
  408. >>> from eth_account import Account
  409. >>> message = encode_defunct(text="I♥SF")
  410. >>> vrs = (
  411. ... 28,
  412. ... '0xe6ca9bba58c88611fad66a6ce8f996908195593807c4b38bd528d2cff09d4eb3',
  413. ... '0x3e5bfbbf4d3e39b1a2fd816a7680c19ebebaf3a141b239934ad43cb33fcec8ce')
  414. >>> Account.recover_message(message, vrs=vrs)
  415. '0x5ce9454909639D2D17A3F753ce7d93fa0b9aB12E'
  416. # All of these recover calls are equivalent:
  417. # variations on vrs
  418. >>> vrs = (
  419. ... '0x1c',
  420. ... '0xe6ca9bba58c88611fad66a6ce8f996908195593807c4b38bd528d2cff09d4eb3',
  421. ... '0x3e5bfbbf4d3e39b1a2fd816a7680c19ebebaf3a141b239934ad43cb33fcec8ce')
  422. >>> Account.recover_message(message, vrs=vrs)
  423. '0x5ce9454909639D2D17A3F753ce7d93fa0b9aB12E'
  424. >>> # Caution about this approach: likely problems if there are leading 0s
  425. >>> vrs = (
  426. ... 0x1c,
  427. ... 0xe6ca9bba58c88611fad66a6ce8f996908195593807c4b38bd528d2cff09d4eb3,
  428. ... 0x3e5bfbbf4d3e39b1a2fd816a7680c19ebebaf3a141b239934ad43cb33fcec8ce)
  429. >>> Account.recover_message(message, vrs=vrs)
  430. '0x5ce9454909639D2D17A3F753ce7d93fa0b9aB12E'
  431. >>> vrs = (
  432. ... b'\x1c',
  433. ... b'\xe6\xca\x9b\xbaX\xc8\x86\x11\xfa\xd6jl\xe8\xf9\x96\x90\x81\x95Y8\x07\xc4\xb3\x8b\xd5(\xd2\xcf\xf0\x9dN\xb3',
  434. ... b'>[\xfb\xbfM>9\xb1\xa2\xfd\x81jv\x80\xc1\x9e\xbe\xba\xf3\xa1A\xb29\x93J\xd4<\xb3?\xce\xc8\xce')
  435. >>> Account.recover_message(message, vrs=vrs)
  436. '0x5ce9454909639D2D17A3F753ce7d93fa0b9aB12E'
  437. # variations on signature
  438. >>> signature = '0xe6ca9bba58c88611fad66a6ce8f996908195593807c4b38bd528d2cff09d4eb33e5bfbbf4d3e39b1a2fd816a7680c19ebebaf3a141b239934ad43cb33fcec8ce1c'
  439. >>> Account.recover_message(message, signature=signature)
  440. '0x5ce9454909639D2D17A3F753ce7d93fa0b9aB12E'
  441. >>> signature = b'\xe6\xca\x9b\xbaX\xc8\x86\x11\xfa\xd6jl\xe8\xf9\x96\x90\x81\x95Y8\x07\xc4\xb3\x8b\xd5(\xd2\xcf\xf0\x9dN\xb3>[\xfb\xbfM>9\xb1\xa2\xfd\x81jv\x80\xc1\x9e\xbe\xba\xf3\xa1A\xb29\x93J\xd4<\xb3?\xce\xc8\xce\x1c'
  442. >>> Account.recover_message(message, signature=signature)
  443. '0x5ce9454909639D2D17A3F753ce7d93fa0b9aB12E'
  444. >>> # Caution about this approach: likely problems if there are leading 0s
  445. >>> signature = 0xe6ca9bba58c88611fad66a6ce8f996908195593807c4b38bd528d2cff09d4eb33e5bfbbf4d3e39b1a2fd816a7680c19ebebaf3a141b239934ad43cb33fcec8ce1c
  446. >>> Account.recover_message(message, signature=signature)
  447. '0x5ce9454909639D2D17A3F753ce7d93fa0b9aB12E'
  448. """ # noqa: E501
  449. message_hash = _hash_eip191_message(signable_message)
  450. return cast(ChecksumAddress, self._recover_hash(message_hash, vrs, signature))
  451. @combomethod
  452. def _recover_hash(
  453. self,
  454. message_hash: Hash32,
  455. vrs: Optional[Tuple[VRS, VRS, VRS]] = None,
  456. signature: Optional[bytes] = None,
  457. ) -> ChecksumAddress:
  458. hash_bytes = HexBytes(message_hash)
  459. if len(hash_bytes) != 32:
  460. raise ValueError("The message hash must be exactly 32-bytes")
  461. if vrs is not None:
  462. v, r, s = map(hexstr_if_str(to_int), vrs)
  463. v_standard = to_standard_v(v)
  464. signature_obj = self._keys.Signature(vrs=(v_standard, r, s))
  465. elif signature is not None:
  466. signature_bytes = HexBytes(signature)
  467. signature_bytes_standard = to_standard_signature_bytes(signature_bytes)
  468. signature_obj = self._keys.Signature(
  469. signature_bytes=signature_bytes_standard
  470. )
  471. else:
  472. raise TypeError("You must supply the vrs tuple or the signature bytes")
  473. pubkey = signature_obj.recover_public_key_from_msg_hash(hash_bytes)
  474. return pubkey.to_checksum_address()
  475. @combomethod
  476. def recover_transaction(
  477. self, serialized_transaction: Union[HexStr, bytes, int]
  478. ) -> ChecksumAddress:
  479. """
  480. Get the address of the account that signed this transaction.
  481. :param serialized_transaction: the complete signed transaction
  482. :type serialized_transaction: hex str, bytes or int
  483. :returns: address of signer, hex-encoded & checksummed
  484. :rtype: ChecksumAddress
  485. .. doctest:: python
  486. >>> raw_transaction = '0xf86a8086d55698372431831e848094f0109fc8df283027b6285cc889f5aa624eac1f55843b9aca008025a009ebb6ca057a0535d6186462bc0b465b561c94a295bdb0621fc19208ab149a9ca0440ffd775ce91a833ab410777204d5341a6f9fa91216a6f3ee2c051fea6a0428'
  487. >>> Account.recover_transaction(raw_transaction)
  488. '0x2c7536E3605D9C16a7a3D7b1898e529396a65c23'
  489. """ # noqa: E501
  490. txn_bytes = HexBytes(serialized_transaction)
  491. if len(txn_bytes) > 0 and txn_bytes[0] <= 0x7F:
  492. # We are dealing with a typed transaction.
  493. typed_transaction = TypedTransaction.from_bytes(txn_bytes)
  494. msg_hash = typed_transaction.hash()
  495. vrs = typed_transaction.vrs()
  496. return cast(ChecksumAddress, self._recover_hash(msg_hash, vrs=vrs))
  497. txn = Transaction.from_bytes(txn_bytes)
  498. msg_hash = hash_of_signed_transaction(txn)
  499. return cast(ChecksumAddress, self._recover_hash(msg_hash, vrs=vrs_from(txn)))
  500. def set_key_backend(
  501. self, backend: Union[CoinCurveECCBackend, NativeECCBackend]
  502. ) -> None:
  503. """
  504. Change the backend used by the underlying eth-keys library.
  505. *(The default is fine for most users)*
  506. :param backend: any backend that works in
  507. `eth_keys.KeyApi(backend)
  508. <https://github.com/ethereum/eth-keys/#keyapibackendnone>`_
  509. """
  510. self._keys = KeyAPI(backend)
  511. @combomethod
  512. def sign_message(
  513. self,
  514. signable_message: SignableMessage,
  515. private_key: PrivateKeyType,
  516. ) -> SignedMessage:
  517. r"""
  518. Sign the provided message.
  519. This API supports any messaging format that will encode to EIP-191 messages.
  520. If you would like historical compatibility with :meth:`w3.eth.sign() <web3.eth.Eth.sign>`
  521. you can use :meth:`~eth_account.messages.encode_defunct`.
  522. Other options are the "validator", or "structured data" standards.
  523. You can import all supported message encoders in
  524. ``eth_account.messages``.
  525. :param signable_message: the encoded message for signing
  526. :param private_key: the key to sign the message with
  527. :type private_key: hex str, bytes, int or :class:`eth_keys.datatypes.PrivateKey`
  528. :returns: Various details about the signature - most importantly the
  529. fields: v, r, and s
  530. :rtype: ~eth_account.datastructures.SignedMessage
  531. .. doctest:: python
  532. >>> msg = "I♥SF"
  533. >>> from eth_account.messages import encode_defunct
  534. >>> msghash = encode_defunct(text=msg)
  535. >>> msghash
  536. SignableMessage(version=b'E',
  537. header=b'thereum Signed Message:\n6',
  538. body=b'I\xe2\x99\xa5SF')
  539. >>> # If you're curious about the internal fields of SignableMessage, take a look at EIP-191, linked above
  540. >>> key = "0xb25c7db31feed9122727bf0939dc769a96564b2de4c4726d035b36ecf1e5b364"
  541. >>> Account.sign_message(msghash, key)
  542. SignedMessage(message_hash=HexBytes('0x1476abb745d423bf09273f1afd887d951181d25adc66c4834a70491911b7f750'),
  543. r=104389933075820307925104709181714897380569894203213074526835978196648170704563,
  544. s=28205917190874851400050446352651915501321657673772411533993420917949420456142,
  545. v=28,
  546. signature=HexBytes('0xe6ca9bba58c88611fad66a6ce8f996908195593807c4b38bd528d2cff09d4eb33e5bfbbf4d3e39b1a2fd816a7680c19ebebaf3a141b239934ad43cb33fcec8ce1c'))
  547. .. _EIP-191: https://eips.ethereum.org/EIPS/eip-191
  548. """ # noqa: E501
  549. message_hash = _hash_eip191_message(signable_message)
  550. return cast(SignedMessage, self._sign_hash(message_hash, private_key))
  551. @combomethod
  552. def unsafe_sign_hash(
  553. self, message_hash: Union[HexStr, bytes, int], private_key: PrivateKeyType
  554. ) -> SignedMessage:
  555. """
  556. Sign the provided hash.
  557. .. WARNING:: *Never* sign a hash that you didn't generate,
  558. it can be an arbitrary transaction. For example, it might
  559. send all of your account's ether to an attacker.
  560. Instead, prefer :meth:`~eth_account.account.Account.sign_message`,
  561. which cannot accidentally sign a transaction.
  562. :param message_hash: the 32-byte message hash to be signed
  563. :type message_hash: hex str, bytes or int
  564. :param private_key: the key to sign the message with
  565. :type private_key: hex str, bytes, int or :class:`eth_keys.datatypes.PrivateKey`
  566. :returns: Various details about the signature - most
  567. importantly the fields: v, r, and s
  568. :rtype: ~eth_account.datastructures.SignedMessage
  569. """
  570. return cast(SignedMessage, self._sign_hash(message_hash, private_key))
  571. @combomethod
  572. def _sign_hash(
  573. self,
  574. message_hash: Hash32,
  575. private_key: PrivateKeyType,
  576. ) -> SignedMessage:
  577. msg_hash_bytes = HexBytes(message_hash)
  578. if len(msg_hash_bytes) != 32:
  579. raise ValueError("The message hash must be exactly 32-bytes")
  580. key = self._parse_private_key(private_key)
  581. (v, r, s, eth_signature_bytes) = sign_message_hash(key, msg_hash_bytes)
  582. return SignedMessage(
  583. message_hash=msg_hash_bytes,
  584. r=r,
  585. s=s,
  586. v=v,
  587. signature=HexBytes(eth_signature_bytes),
  588. )
  589. @combomethod
  590. def sign_transaction(
  591. self,
  592. transaction_dict: TransactionDictType,
  593. private_key: PrivateKeyType,
  594. blobs: Optional[Blobs] = None,
  595. ) -> SignedTransaction:
  596. r"""
  597. Sign a transaction using a local private key.
  598. It produces signature details and the hex-encoded transaction suitable for
  599. broadcast using :meth:`w3.eth.send_raw_transaction()<web3.eth.Eth.send_raw_transaction>`.
  600. To create the transaction dict that calls a contract, use
  601. :meth:`my_contract.functions.myFunction().build_transaction()<web3.contract.ContractFunction.build_transaction>`.
  602. Note: For non-legacy (typed) transactions, if the transaction type is not
  603. explicitly provided, it may be determined from the transaction parameters of
  604. a well-formed transaction. See below for examples on how to sign with
  605. different transaction types.
  606. :param dict transaction_dict: the transaction with available keys, depending
  607. on the type of transaction: nonce, chainId, to, data, value, gas, gasPrice,
  608. type, accessList, maxFeePerGas, and maxPriorityFeePerGas
  609. :param private_key: the private key to sign the data with
  610. :type private_key: hex str, bytes, int or :class:`eth_keys.datatypes.PrivateKey`
  611. :param blobs: optional list of blobs to sign in addition to the transaction
  612. :type blobs: list of bytes or HexBytes
  613. :returns: Various details about the signature - most
  614. importantly the fields: v, r, and s
  615. :rtype: SignedTransaction
  616. .. doctest:: python
  617. >>> # EIP-1559 dynamic fee transaction (more efficient and preferred over legacy txn)
  618. >>> from eth_account import Account
  619. >>> dynamic_fee_transaction = {
  620. ... "type": 2, # optional - can be implicitly determined based on max fee params
  621. ... "gas": 100000,
  622. ... "maxFeePerGas": 2000000000,
  623. ... "maxPriorityFeePerGas": 2000000000,
  624. ... "data": "0x616263646566",
  625. ... "nonce": 34,
  626. ... "to": "0x09616C3d61b3331fc4109a9E41a8BDB7d9776609",
  627. ... "value": "0x5af3107a4000",
  628. ... "accessList": ( # optional
  629. ... {
  630. ... "address": "0x0000000000000000000000000000000000000001",
  631. ... "storageKeys": (
  632. ... "0x0100000000000000000000000000000000000000000000000000000000000000",
  633. ... )
  634. ... },
  635. ... ),
  636. ... "chainId": 1337,
  637. ... }
  638. >>> key = '0x4c0883a69102937d6231471b5dbb6204fe5129617082792ae468d01a3f362318'
  639. >>> signed_df_tx = Account.sign_transaction(dynamic_fee_transaction, key)
  640. >>> signed_df_tx
  641. SignedTransaction(raw_transaction=HexBytes('0x02f8b28205392284773594008477359400830186a09409616c3d61b3331fc4109a9e41a8bdb7d9776609865af3107...d58b85d5'),
  642. hash=HexBytes('0x2721b2ac99d878695e410af9e8968859b6f6e94f544840be0eb2935bead7deba'),
  643. r=48949965662841329840326477994465373664672499148507933176648302825256944281697,
  644. s=1123041608316060268133200864147951676126406077675157976022772782796802590165,
  645. v=1)
  646. >>> w3.eth.send_raw_transaction(signed_df_tx.raw_transaction) # doctest: +SKIP
  647. .. doctest:: python
  648. >>> # legacy transaction (less efficient than EIP-1559 dynamic fee txn)
  649. >>> from eth_account import Account
  650. >>> legacy_transaction = {
  651. ... # Note that the address must be in checksum format or native bytes:
  652. ... 'to': '0xF0109fC8DF283027b6285cc889F5aA624EaC1F55',
  653. ... 'value': 1000000000,
  654. ... 'gas': 2000000,
  655. ... 'gasPrice': 234567897654321,
  656. ... 'nonce': 0,
  657. ... 'chainId': 1337
  658. ... }
  659. >>> key = '0x4c0883a69102937d6231471b5dbb6204fe5129617082792ae468d01a3f362318'
  660. >>> signed_legacy_tx = Account.sign_transaction(legacy_transaction, key)
  661. >>> signed_legacy_tx
  662. SignedTransaction(raw_transaction=HexBytes('0xf86c8086d55698372431831e848094f0109fc8df283027b6285cc889f5aa624eac1f55843b9aca0080820a95a01a7...c0bfdb52'),
  663. hash=HexBytes('0xd0a3e5dc7439f260c64cb0220139ec5dc7e016f82ce272a25a0f0b38fe751673'),
  664. r=11971260903864915610009019893820767192081275151191539081612245320300335068143,
  665. s=35365272040292958794699923036506252105590820339897221552886630515981233937234,
  666. v=2709)
  667. >>> w3.eth.send_raw_transaction(signed_legacy_tx.raw_transaction) # doctest: +SKIP
  668. .. doctest:: python
  669. >>> from eth_account import Account
  670. >>> access_list_transaction = {
  671. ... "type": 1, # optional - can be implicitly determined based on 'accessList' and 'gasPrice' params
  672. ... "gas": 100000,
  673. ... "gasPrice": 1000000000,
  674. ... "data": "0x616263646566",
  675. ... "nonce": 34,
  676. ... "to": "0x09616C3d61b3331fc4109a9E41a8BDB7d9776609",
  677. ... "value": "0x5af3107a4000",
  678. ... "accessList": (
  679. ... {
  680. ... "address": "0x0000000000000000000000000000000000000001",
  681. ... "storageKeys": (
  682. ... "0x0100000000000000000000000000000000000000000000000000000000000000",
  683. ... )
  684. ... },
  685. ... ),
  686. ... "chainId": 1337,
  687. ... }
  688. >>> key = '0x4c0883a69102937d6231471b5dbb6204fe5129617082792ae468d01a3f362318'
  689. >>> signed_al_tx = Account.sign_transaction(access_list_transaction, key)
  690. >>> signed_al_tx
  691. SignedTransaction(raw_transaction=HexBytes('0x01f8ad82053922843b9aca00830186a09409616c3d61b3331fc4109a9e41a8bdb7d9776609865af3107a400086616...2b5043ea'),
  692. hash=HexBytes('0xca9af2ef41691e06eb07e02125938fd9bb5a311e8daf330b264e77d6cdf3d17e'),
  693. r=107355854401379915513092408112372039746594668141865279802319959599514133709188,
  694. s=6729502936685237038651223791038758905953302464070244934323623239104475448298,
  695. v=1)
  696. >>> w3.eth.send_raw_transaction(signed_al_tx.raw_transaction) # doctest: +SKIP
  697. .. doctest:: python
  698. >>> from eth_account import Account
  699. >>> blob_transaction = {
  700. ... "type": 3, # optional - can be implicitly determined based on `maxFeePerBlobGas` param
  701. ... "gas": 100000,
  702. ... "maxFeePerGas": 2000000000,
  703. ... "maxPriorityFeePerGas": 2000000000,
  704. ... "maxFeePerBlobGas": 2000000000,
  705. ... "data": "0x616263646566",
  706. ... "nonce": 34,
  707. ... "to": "0x09616C3d61b3331fc4109a9E41a8BDB7d9776609",
  708. ... "value": "0x5af3107a4000",
  709. ... "accessList": ( # optional
  710. ... {
  711. ... "address": "0x0000000000000000000000000000000000000001",
  712. ... "storageKeys": (
  713. ... "0x0100000000000000000000000000000000000000000000000000000000000000",
  714. ... )
  715. ... },
  716. ... ),
  717. ... "chainId": 1337,
  718. ... }
  719. >>> empty_blob = b"\x00" * 32 * 4096 # 4096 empty 32-byte field elements
  720. >>> key = '0x4c0883a69102937d6231471b5dbb6204fe5129617082792ae468d01a3f362318'
  721. >>> # The `blobVersionedHashes` transaction field is calculated from the `blobs` kwarg
  722. >>> signed_blob_tx = Account.sign_transaction(blob_transaction, key, blobs=[empty_blob])
  723. >>> signed_blob_tx
  724. SignedTransaction(raw_transaction=HexBytes('0x03fa020147f8d98205392284773594008477359400830186a09409616c3d61b3331fc4109a9e41a8bdb7d97766098...00000000'),
  725. hash=HexBytes('0xf9dc8867c4324fd7f4506622aa700989562770f01d7d681cef74a1a1deb9fea9'),
  726. r=14319949980593194209648175507603206696573324965145502821772573913457715875718,
  727. s=9129184742597516615341309773045281461399831333162885393648678700392065987233,
  728. v=1)
  729. >>> w3.eth.send_raw_transaction(signed_blob_tx.raw_transaction) # doctest: +SKIP
  730. """ # noqa: E501
  731. if not isinstance(transaction_dict, Mapping):
  732. raise TypeError(
  733. f"transaction_dict must be dict-like, got {repr(transaction_dict)}"
  734. )
  735. account = self.from_key(private_key)
  736. # allow from field, *only* if it matches the private key
  737. if "from" in transaction_dict:
  738. if transaction_dict["from"] == account.address:
  739. sanitized_transaction = dissoc(transaction_dict, "from")
  740. else:
  741. str_from = (
  742. transaction_dict["from"].decode()
  743. if isinstance(transaction_dict["from"], bytes)
  744. else transaction_dict["from"]
  745. )
  746. raise TypeError(
  747. f"from field must match key's {account.address}, but it was "
  748. f"{str_from}"
  749. )
  750. else:
  751. sanitized_transaction = transaction_dict
  752. # sign transaction
  753. (
  754. v,
  755. r,
  756. s,
  757. encoded_transaction,
  758. ) = sign_transaction_dict(account._key_obj, sanitized_transaction, blobs=blobs)
  759. transaction_hash = keccak(encoded_transaction)
  760. return SignedTransaction(
  761. raw_transaction=HexBytes(encoded_transaction),
  762. hash=HexBytes(transaction_hash),
  763. r=r,
  764. s=s,
  765. v=v,
  766. )
  767. @combomethod
  768. def _parse_private_key(
  769. self,
  770. key: PrivateKeyType,
  771. ) -> PrivateKey:
  772. """
  773. Generate a :class:`eth_keys.datatypes.PrivateKey` from the provided key.
  774. If the key is already of type :class:`eth_keys.datatypes.PrivateKey`,
  775. return the key.
  776. :param key: the private key from which a :class:`eth_keys.datatypes.PrivateKey`
  777. will be generated
  778. :type key: hex str, bytes, int or :class:`eth_keys.datatypes.PrivateKey`
  779. :returns: the provided key represented as a
  780. :class:`eth_keys.datatypes.PrivateKey`
  781. """
  782. if isinstance(key, self._keys.PrivateKey):
  783. return key
  784. hb_key = HexBytes(key)
  785. try:
  786. return self._keys.PrivateKey(hb_key)
  787. except ValidationError as original_exception:
  788. raise ValueError(
  789. "The private key must be exactly 32 bytes long, instead of "
  790. f"{len(hb_key)} bytes."
  791. ) from original_exception
  792. @combomethod
  793. def sign_typed_data(
  794. self,
  795. private_key: PrivateKeyType,
  796. domain_data: Optional[Dict[str, Any]] = None,
  797. message_types: Optional[Dict[str, Any]] = None,
  798. message_data: Optional[Dict[str, Any]] = None,
  799. full_message: Optional[Dict[str, Any]] = None,
  800. ) -> SignedMessage:
  801. r"""
  802. Sign the provided EIP-712 message with the provided key.
  803. :param private_key: the key to sign the message with
  804. :param domain_data: EIP712 domain data
  805. :param message_types: custom types used by the `value` data
  806. :param message_data: data to be signed
  807. :param full_message: a dict containing all data and types
  808. :type private_key: hex str, bytes, int or :class:`eth_keys.datatypes.PrivateKey`
  809. :type domain_data: dict
  810. :type message_types: dict
  811. :type message_data: dict
  812. :type full_message: dict
  813. :returns: Various details about the signature - most importantly the
  814. fields: v, r, and s
  815. :rtype: ~eth_account.datastructures.SignedMessage
  816. You may supply the information to be encoded in one of two ways:
  817. As exactly three arguments:
  818. - ``domain_data``, a dict of the EIP-712 domain data
  819. - ``message_types``, a dict of custom types (do not include a ``EIP712Domain``
  820. key)
  821. - ``message_data``, a dict of the data to be signed
  822. Or as a single argument:
  823. - ``full_message``, a dict containing the following keys:
  824. - ``types``, a dict of custom types (may include a ``EIP712Domain`` key)
  825. - ``primaryType``, (optional) a string of the primary type of the message
  826. - ``domain``, a dict of the EIP-712 domain data
  827. - ``message``, a dict of the data to be signed
  828. .. WARNING:: Note that this code has not gone through an external audit, and
  829. the test cases are incomplete.
  830. See documentation for :meth:`~eth_account.messages.encode_typed_data` for usage details
  831. See the `EIP-712 spec <https://eips.ethereum.org/EIPS/eip-712>`_ for more information.
  832. .. doctest:: python
  833. >>> # examples of basic usage
  834. >>> from eth_account import Account
  835. >>> # 3-argument usage
  836. >>> # all domain properties are optional
  837. >>> domain_data = {
  838. ... "name": "Ether Mail",
  839. ... "version": "1",
  840. ... "chainId": 1,
  841. ... "verifyingContract": "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC",
  842. ... "salt": b"decafbeef",
  843. ... }
  844. >>> # custom types
  845. >>> message_types = {
  846. ... "Person": [
  847. ... {"name": "name", "type": "string"},
  848. ... {"name": "wallet", "type": "address"},
  849. ... ],
  850. ... "Mail": [
  851. ... {"name": "from", "type": "Person"},
  852. ... {"name": "to", "type": "Person"},
  853. ... {"name": "contents", "type": "string"},
  854. ... ],
  855. ... }
  856. >>> # the data to be signed
  857. >>> message_data = {
  858. ... "from": {
  859. ... "name": "Cow",
  860. ... "wallet": "0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826",
  861. ... },
  862. ... "to": {
  863. ... "name": "Bob",
  864. ... "wallet": "0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB",
  865. ... },
  866. ... "contents": "Hello, Bob!",
  867. ... }
  868. >>> key = "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
  869. >>> signed_message = Account.sign_typed_data(key, domain_data, message_types, message_data)
  870. >>> signed_message.message_hash
  871. HexBytes('0xc5bb16ccc59ae9a3ad1cb8343d4e3351f057c994a97656e1aff8c134e56f7530')
  872. >>> # 1-argument usage
  873. >>> # all domain properties are optional
  874. >>> full_message = {
  875. ... "types": {
  876. ... "EIP712Domain": [
  877. ... {"name": "name", "type": "string"},
  878. ... {"name": "version", "type": "string"},
  879. ... {"name": "chainId", "type": "uint256"},
  880. ... {"name": "verifyingContract", "type": "address"},
  881. ... {"name": "salt", "type": "bytes32"},
  882. ... ],
  883. ... "Person": [
  884. ... {"name": "name", "type": "string"},
  885. ... {"name": "wallet", "type": "address"},
  886. ... ],
  887. ... "Mail": [
  888. ... {"name": "from", "type": "Person"},
  889. ... {"name": "to", "type": "Person"},
  890. ... {"name": "contents", "type": "string"},
  891. ... ],
  892. ... },
  893. ... "primaryType": "Mail",
  894. ... "domain": {
  895. ... "name": "Ether Mail",
  896. ... "version": "1",
  897. ... "chainId": 1,
  898. ... "verifyingContract": "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC",
  899. ... "salt": b"decafbeef"
  900. ... },
  901. ... "message": {
  902. ... "from": {
  903. ... "name": "Cow",
  904. ... "wallet": "0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826"
  905. ... },
  906. ... "to": {
  907. ... "name": "Bob",
  908. ... "wallet": "0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB"
  909. ... },
  910. ... "contents": "Hello, Bob!",
  911. ... },
  912. ... }
  913. >>> signed_message_2 = Account.sign_typed_data(key, full_message=full_message)
  914. >>> signed_message_2.message_hash
  915. HexBytes('0xc5bb16ccc59ae9a3ad1cb8343d4e3351f057c994a97656e1aff8c134e56f7530')
  916. >>> signed_message_2 == signed_message
  917. True
  918. .. _EIP-712: https://eips.ethereum.org/EIPS/eip-712
  919. """ # noqa: E501
  920. signable_message = encode_typed_data(
  921. domain_data,
  922. message_types,
  923. message_data,
  924. full_message,
  925. )
  926. message_hash = _hash_eip191_message(signable_message)
  927. return cast(SignedMessage, self._sign_hash(message_hash, private_key))
  928. @combomethod
  929. def sign_authorization(
  930. self,
  931. authorization_dict: AuthorizationDict,
  932. private_key: PrivateKeyType,
  933. ) -> SignedSetCodeAuthorization:
  934. r"""
  935. Sign an authorization using a local private key to be included in a EIP-7702 transaction.
  936. :param dict authorization_dict: the required keys are: ``chainId``, ``address``, and ``nonce``
  937. :param private_key: the private key to sign the data with
  938. :type private_key: hex str, bytes, int or :class:`eth_keys.datatypes.PrivateKey`
  939. :returns: the dictionary with the signature fields added, suitable for inclusion in a EIP-7702 transaction
  940. :rtype: dict
  941. .. NOTE::
  942. You need to sign one or more authorizations from an EOA willing to have a smart contract code associated with the EOA for the life of the transaction.
  943. The variable ``auth`` in the code below is the authorization dictionary containing the following keys:
  944. - ``chainId`` is the chain id for the chain where the EOA is located. If ``0`` is specified, authorization is signed for all chains
  945. - ``address`` is the address of the smart contract code to be associated with the EOA, as bytes
  946. - ``nonce`` is the nonce of the EOA, used to prevent replay attacks
  947. To create a transaction that associates the code with the EOA, you need to create a transaction with an ``authorizationList``, representing a list of signed authorizations for the transaction.
  948. .. doctest:: python
  949. >>> from eth_account import Account
  950. >>> key = "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
  951. >>> auth = {
  952. ... "chainId": 1337,
  953. ... "address": "0x5ce9454909639d2d17a3f753ce7d93fa0b9ab12e",
  954. ... # If the tx sender is the same as the authority, the nonce needs to be 1 higher than the current (transaction) nonce.
  955. ... # This is because the nonce is increased before tx execution and again when processing the authorization.
  956. ... "nonce": 1,
  957. ... }
  958. >>> signed_auth = Account.sign_authorization(auth, key)
  959. >>> signed_auth
  960. SignedSetCodeAuthorization(chain_id=1337,
  961. address=b'\\\xe9EI\tc\x9d-\x17\xa3\xf7S\xce}\x93\xfa\x0b\x9a\xb1.',
  962. nonce=1,
  963. y_parity=0,
  964. r=52163433520757118830640642673035732532535423029712132518776649895118143897479,
  965. s=57576671166887700066365341925867052133948674355067837907255957076179513983345,
  966. signature='0x735375048fc96b87390b5a11c411fc57245d8e55038bf49e659d048a0d1a3f877f4b3db448845cb217812f23c6b345e99f2d21c44ec10e93a8f039814167417100',
  967. authorization_hash=HexBytes('0x9026f77ed6740d6d08f0cdc0591a86b2232700020a816718fbf760785e9ca2f2'))
  968. >>> tx = {
  969. ... "gas": 100000,
  970. ... "maxFeePerGas": 2000000000,
  971. ... "maxPriorityFeePerGas": 2000000000,
  972. ... "data": "0x252dba42", # replace with calldata for the contract at the auth ``address``
  973. ... "nonce": 0,
  974. ... "to": "0x09616C3d61b3331fc4109a9E41a8BDB7d9776609",
  975. ... "value": 0,
  976. ... "accessList": (),
  977. ... "authorizationList": [signed_auth],
  978. ... "chainId": 1337,
  979. ... }
  980. >>> signed = Account.sign_transaction(tx, key)
  981. >>> w3.eth.send_raw_transaction(signed.raw_transaction) # doctest: +SKIP
  982. .. _EIP-7702: https://eips.ethereum.org/EIPS/eip-7702
  983. """ # noqa: E501
  984. if not isinstance(authorization_dict, Mapping):
  985. raise TypeError(
  986. f"authorization_dict must be dict-like, got {repr(authorization_dict)}"
  987. )
  988. authority_key = self._parse_private_key(private_key)
  989. # prevent mutating the original input
  990. authorization_dict = copy(authorization_dict)
  991. chain_id = authorization_dict["chainId"]
  992. code_address = to_canonical_address(authorization_dict["address"])
  993. nonce = authorization_dict["nonce"]
  994. unsigned_authorization = Authorization(chain_id, code_address, nonce)
  995. authorization_hash = unsigned_authorization.hash()
  996. signature = authority_key.sign_msg_hash(authorization_hash)
  997. [v, r, s] = signature.vrs
  998. return SignedSetCodeAuthorization(
  999. chain_id=chain_id,
  1000. address=code_address,
  1001. nonce=nonce,
  1002. y_parity=v,
  1003. r=r,
  1004. s=s,
  1005. signature=signature,
  1006. authorization_hash=authorization_hash,
  1007. )