TokenImplementation.t.sol 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956
  1. // SPDX-License-Identifier: Apache 2
  2. pragma solidity ^0.8.0;
  3. import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
  4. import "../contracts/bridge/token/TokenImplementation.sol";
  5. import "forge-std/Test.sol";
  6. import "forge-std/console.sol";
  7. contract TestTokenImplementation is TokenImplementation, Test {
  8. uint256 constant SECP256K1_CURVE_ORDER =
  9. 115792089237316195423570985008687907852837564279074904382605163141518161494337;
  10. struct InitiateParameters {
  11. string name;
  12. string symbol;
  13. uint8 decimals;
  14. uint64 sequence;
  15. address owner;
  16. uint16 chainId;
  17. bytes32 nativeContract;
  18. }
  19. struct SignatureSetup {
  20. address allower;
  21. bytes32 r;
  22. bytes32 s;
  23. uint8 v;
  24. }
  25. function setupTestEnvironmentWithInitialize() public {
  26. InitiateParameters memory init;
  27. init.name = "Valuable Token";
  28. init.symbol = "VALU";
  29. init.decimals = 8;
  30. init.sequence = 1;
  31. init.owner = _msgSender();
  32. init.chainId = 5;
  33. init
  34. .nativeContract = 0x1337133713371337133713371337133713371337133713371337133713371337;
  35. initialize(
  36. init.name,
  37. init.symbol,
  38. init.decimals,
  39. init.sequence,
  40. init.owner,
  41. init.chainId,
  42. init.nativeContract
  43. );
  44. }
  45. function setupTestEnvironmentWithOldInitialize() public {
  46. InitiateParameters memory init;
  47. init.name = "Old Valuable Token";
  48. init.symbol = "OLD";
  49. init.decimals = 8;
  50. init.sequence = 1;
  51. init.owner = _msgSender();
  52. init.chainId = 5;
  53. init
  54. .nativeContract = 0x1337133713371337133713371337133713371337133713371337133713371337;
  55. _initializeNativeToken(
  56. init.name,
  57. init.symbol,
  58. init.decimals,
  59. init.sequence,
  60. init.owner,
  61. init.chainId,
  62. init.nativeContract
  63. );
  64. }
  65. function simulatePermitSignature(
  66. bytes32 walletPrivateKey,
  67. address spender,
  68. uint256 amount,
  69. uint256 deadline
  70. ) public view returns (SignatureSetup memory output) {
  71. // prepare signer allowing for tokens to be spent
  72. uint256 sk = uint256(walletPrivateKey);
  73. output.allower = vm.addr(sk);
  74. bytes32 PERMIT_TYPEHASH = keccak256(
  75. "Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"
  76. );
  77. bytes32 structHash = keccak256(
  78. abi.encode(
  79. PERMIT_TYPEHASH,
  80. output.allower,
  81. spender,
  82. amount,
  83. nonces(output.allower),
  84. deadline
  85. )
  86. );
  87. bytes32 message = ECDSA.toTypedDataHash(DOMAIN_SEPARATOR(), structHash);
  88. (output.v, output.r, output.s) = vm.sign(sk, message);
  89. }
  90. // if any of these tests fail, you may have messed around with the
  91. // existing storage slots
  92. function testCheckStorageSlots() public {
  93. // initialize TokenImplementation
  94. setupTestEnvironmentWithInitialize();
  95. // mint some so we can check totalSupply and balances
  96. uint256 mintedAmount = 42069;
  97. _mint(_msgSender(), mintedAmount);
  98. // also set allowances
  99. uint256 allowanceAmount = 69420;
  100. address spender = address(0x1);
  101. _approve(_msgSender(), spender, allowanceAmount);
  102. // slot 0: name (string)
  103. {
  104. bytes32 data = vm.load(address(this), bytes32(0));
  105. // length 14, name = "Valuable Token"
  106. // data <= 31 bytes long, so length is stored at end as length * 2
  107. bytes memory expectedName = bytes("Valuable Token");
  108. require(
  109. uint256(data) & uint256(255) == expectedName.length * 2,
  110. "incorrect name length"
  111. );
  112. require(
  113. uint256(data) & uint256(255) == bytes(_state.name).length * 2,
  114. "incorrect name length"
  115. );
  116. for (uint256 i = 0; i < expectedName.length; ++i) {
  117. // I don't care to save this variable to storage
  118. require(
  119. data[i] == expectedName[i],
  120. "data[i] != expectedName[i]"
  121. );
  122. require(
  123. data[i] == bytes(_state.name)[i],
  124. "data[i] != _state.name[i]"
  125. );
  126. }
  127. }
  128. // slot 1: symbol (string)
  129. {
  130. bytes32 data = vm.load(address(this), bytes32(uint256(1)));
  131. // length 14, name = "Valuable Token"
  132. // data <= 31 bytes long, so length is stored at end as length * 2
  133. bytes memory expectedSymbol = bytes("VALU");
  134. require(
  135. uint256(data) & uint256(255) == expectedSymbol.length * 2,
  136. "incorrect symbol length"
  137. );
  138. require(
  139. uint256(data) & uint256(255) == bytes(_state.symbol).length * 2,
  140. "incorrect symbol length"
  141. );
  142. for (uint256 i = 0; i < expectedSymbol.length; ++i) {
  143. // I don't care to save this variable to storage
  144. require(
  145. data[i] == expectedSymbol[i],
  146. "data[i] != expectedSymbol[i]"
  147. );
  148. require(
  149. data[i] == bytes(_state.symbol)[i],
  150. "data[i] != _state.symbol[i]"
  151. );
  152. }
  153. }
  154. // slot 2: metaLastUpdatedSequence (uint64)
  155. {
  156. bytes32 data = vm.load(address(this), bytes32(uint256(2)));
  157. require(
  158. uint256(data) == uint256(1),
  159. "data != expected metaLastUpdatedSequence"
  160. );
  161. require(
  162. uint256(data) == uint256(_state.metaLastUpdatedSequence),
  163. "data != _state.metaLastUpdatedSequence"
  164. );
  165. }
  166. // slot 3: totalSupply (uint256)
  167. {
  168. // now verify
  169. bytes32 data = vm.load(address(this), bytes32(uint256(3)));
  170. require(
  171. uint256(data) == mintedAmount,
  172. "data != expected totalSupply"
  173. );
  174. require(
  175. uint256(data) == uint256(_state.totalSupply),
  176. "data != _state.totalSupply"
  177. );
  178. }
  179. // slot 4: decimals (uint8)
  180. {
  181. bytes32 data = vm.load(address(this), bytes32(uint256(4)));
  182. require(uint256(data) == uint256(8), "data != expected decimals");
  183. require(
  184. uint256(data) == uint256(_state.decimals),
  185. "data != _state.decimals"
  186. );
  187. }
  188. // slot 5: balances (mapping(address) => uint256)
  189. {
  190. bytes32 data = vm.load(address(this), bytes32(uint256(5)));
  191. require(uint256(data) == uint256(0), "data != 0");
  192. bytes32 mappedData = vm.load(
  193. address(this),
  194. keccak256(abi.encode(_msgSender(), 5))
  195. );
  196. require(
  197. uint256(mappedData) == mintedAmount,
  198. "data != expected balance for account"
  199. );
  200. require(
  201. uint256(mappedData) == _state.balances[_msgSender()],
  202. "data != _state.balances[_msgSender()]"
  203. );
  204. }
  205. // slot 6: allowances (mapping(address) => uint256)
  206. {
  207. bytes32 data = vm.load(address(this), bytes32(uint256(6)));
  208. require(uint256(data) == uint256(0), "data != 0");
  209. bytes32 mappedData = vm.load(
  210. address(this),
  211. keccak256(
  212. abi.encode(spender, keccak256(abi.encode(_msgSender(), 6)))
  213. )
  214. );
  215. require(
  216. uint256(mappedData) == allowanceAmount,
  217. "data != expected allowance for account"
  218. );
  219. require(
  220. uint256(mappedData) == _state.allowances[_msgSender()][spender],
  221. "data != _state.allowances[_msgSender()][spender]"
  222. );
  223. }
  224. // slot 7: owner (address), initialized (bool), chainId (uint16)
  225. {
  226. bytes32 data = vm.load(address(this), bytes32(uint256(7)));
  227. require(
  228. (uint256(data) >> (21 * 8)) == uint256(5),
  229. "data[9:11] != expected chainId"
  230. );
  231. require(
  232. (uint256(data) >> (21 * 8)) == uint256(_state.chainId),
  233. "data[9:11] != _state.chainId"
  234. );
  235. require(
  236. uint8(data[11]) == uint8(1),
  237. "data[11] != expected initialized"
  238. );
  239. require(
  240. uint8(data[11]) == uint8(_state.initialized ? 1 : 0),
  241. "data[11] != _state.initialized"
  242. );
  243. require(
  244. uint256(data) & uint256(2**160 - 1) ==
  245. uint256(uint160(_msgSender())),
  246. "data[12:32] != expected owner"
  247. );
  248. require(
  249. uint256(data) & uint256(2**160 - 1) ==
  250. uint256(uint160(_state.owner)),
  251. "data[12:32] != _state.owner"
  252. );
  253. }
  254. // slot 8: nativeContract (bytes32)
  255. {
  256. bytes32 data = vm.load(address(this), bytes32(uint256(8)));
  257. require(
  258. data ==
  259. 0x1337133713371337133713371337133713371337133713371337133713371337,
  260. "data != expected nativeContract"
  261. );
  262. require(
  263. data == _state.nativeContract,
  264. "data != _state.nativeContract"
  265. );
  266. }
  267. // slot 9: cachedDomainSeparator (bytes32)
  268. {
  269. bytes32 data = vm.load(address(this), bytes32(uint256(9)));
  270. require(
  271. data ==
  272. _buildDomainSeparator(
  273. _eip712DomainNameHashed(),
  274. _eip712DomainSalt()
  275. ),
  276. "data != expected domain separator"
  277. );
  278. require(data == DOMAIN_SEPARATOR(), "data != DOMAIN_SEPARATOR()");
  279. require(
  280. data == _state.cachedDomainSeparator,
  281. "data != _state.cachedDomainSeparator"
  282. );
  283. }
  284. // slot 10: cachedChainId (uint256)
  285. {
  286. bytes32 data = vm.load(address(this), bytes32(uint256(10)));
  287. require(uint256(data) == block.chainid, "data != block.chainid");
  288. require(
  289. uint256(data) == _state.cachedChainId,
  290. "data != _state.cachedChainId"
  291. );
  292. }
  293. // slot 11: cachedThis (address)
  294. {
  295. bytes32 data = vm.load(address(this), bytes32(uint256(11)));
  296. require(
  297. uint256(data) == uint256(uint160(address(this))),
  298. "data != address(this)"
  299. );
  300. require(
  301. uint256(data) == uint256(uint160(_state.cachedThis)),
  302. "data != _state.cachedThis"
  303. );
  304. }
  305. // slot 12: cachedSalt (bytes32)
  306. {
  307. bytes32 data = vm.load(address(this), bytes32(uint256(12)));
  308. require(data == _eip712DomainSalt(), "data != expected salt");
  309. require(data == _state.cachedSalt, "data != _state.cachedSalt");
  310. }
  311. // slot 13: cachedHashedName (bytes32)
  312. {
  313. bytes32 data = vm.load(address(this), bytes32(uint256(13)));
  314. require(
  315. data == _eip712DomainNameHashed(),
  316. "data != _eip712DomainNameHashed()"
  317. );
  318. require(
  319. data == _state.cachedHashedName,
  320. "data != _state.cachedHashedName"
  321. );
  322. }
  323. }
  324. function testPermit(
  325. bytes32 walletPrivateKey,
  326. uint256 amount,
  327. address spender
  328. ) public {
  329. vm.assume(walletPrivateKey != bytes32(0));
  330. vm.assume(uint256(walletPrivateKey) < SECP256K1_CURVE_ORDER);
  331. vm.assume(spender != address(0));
  332. // initialize TokenImplementation
  333. setupTestEnvironmentWithInitialize();
  334. // prepare signer allowing for tokens to be spent
  335. uint256 deadline = 10;
  336. SignatureSetup memory signature = simulatePermitSignature(
  337. walletPrivateKey,
  338. spender,
  339. amount,
  340. deadline
  341. );
  342. // set allowance with permit
  343. permit(
  344. signature.allower,
  345. spender,
  346. amount,
  347. deadline,
  348. signature.v,
  349. signature.r,
  350. signature.s
  351. );
  352. require(
  353. allowance(signature.allower, spender) == amount,
  354. "allowance incorrect"
  355. );
  356. }
  357. function test_Revert_PermitWithSameSignature(
  358. bytes32 walletPrivateKey,
  359. uint256 amount,
  360. address spender
  361. ) public {
  362. vm.assume(walletPrivateKey != bytes32(0));
  363. vm.assume(uint256(walletPrivateKey) < SECP256K1_CURVE_ORDER);
  364. vm.assume(spender != address(0));
  365. // initialize TokenImplementation
  366. setupTestEnvironmentWithInitialize();
  367. // prepare signer allowing for tokens to be spent
  368. uint256 deadline = 10;
  369. SignatureSetup memory signature = simulatePermitSignature(
  370. walletPrivateKey,
  371. spender,
  372. amount,
  373. deadline
  374. );
  375. // set allowance with permit
  376. permit(
  377. signature.allower,
  378. spender,
  379. amount,
  380. deadline,
  381. signature.v,
  382. signature.r,
  383. signature.s
  384. );
  385. vm.expectRevert("ERC20Permit: invalid signature");
  386. this.permit(
  387. signature.allower,
  388. spender,
  389. amount,
  390. deadline,
  391. signature.v,
  392. signature.r,
  393. signature.s
  394. );
  395. }
  396. function test_Revert_PermitWithBadSignature(
  397. bytes32 walletPrivateKey,
  398. uint256 amount,
  399. address spender
  400. ) public {
  401. vm.assume(walletPrivateKey != bytes32(0));
  402. vm.assume(uint256(walletPrivateKey) < SECP256K1_CURVE_ORDER);
  403. vm.assume(spender != address(0));
  404. // initialize TokenImplementation
  405. setupTestEnvironmentWithInitialize();
  406. // avoid overflow for this test
  407. uint256 wrongAmount;
  408. unchecked {
  409. wrongAmount = amount + 1; // amount will never equal
  410. }
  411. // prepare signer allowing for tokens to be spent
  412. uint256 deadline = 10;
  413. SignatureSetup memory signature = simulatePermitSignature(
  414. walletPrivateKey,
  415. spender,
  416. wrongAmount,
  417. deadline
  418. );
  419. // you shall not pass!
  420. vm.expectRevert("ERC20Permit: invalid signature");
  421. this.permit(
  422. signature.allower,
  423. spender,
  424. amount,
  425. deadline,
  426. signature.v,
  427. signature.r,
  428. signature.s
  429. );
  430. }
  431. function testPermitWithSignatureUsedAfterDeadline(
  432. bytes32 walletPrivateKey,
  433. uint256 amount,
  434. address spender
  435. ) public {
  436. vm.assume(walletPrivateKey != bytes32(0));
  437. vm.assume(uint256(walletPrivateKey) < SECP256K1_CURVE_ORDER);
  438. vm.assume(spender != address(0));
  439. // initialize TokenImplementation
  440. setupTestEnvironmentWithInitialize();
  441. // prepare signer allowing for tokens to be spent
  442. uint256 deadline = 10;
  443. SignatureSetup memory signature = simulatePermitSignature(
  444. walletPrivateKey,
  445. spender,
  446. amount,
  447. deadline
  448. );
  449. // waited too long
  450. vm.warp(deadline + 1);
  451. // and fail
  452. vm.expectRevert("ERC20Permit: expired deadline");
  453. this.permit(
  454. signature.allower,
  455. spender,
  456. amount,
  457. deadline,
  458. signature.v,
  459. signature.r,
  460. signature.s
  461. );
  462. }
  463. function testInitializePermitState() public {
  464. // initialize TokenImplementation as if it were the old implementation
  465. setupTestEnvironmentWithOldInitialize();
  466. require(
  467. _state.cachedHashedName == bytes32(0),
  468. "cachedHashedName is set"
  469. );
  470. require(_state.cachedSalt == bytes32(0), "cachedSalt is set");
  471. // explicity call private method
  472. _initializePermitStateIfNeeded();
  473. require(
  474. _state.cachedHashedName == _eip712DomainNameHashed(),
  475. "hasnedName not cached"
  476. );
  477. require(_state.cachedSalt == _eip712DomainSalt(), "salt not cached");
  478. // check permit state variables
  479. require(
  480. _state.cachedChainId == block.chainid,
  481. "_state.cachedChainId != expected"
  482. );
  483. require(
  484. _state.cachedDomainSeparator ==
  485. keccak256(
  486. abi.encode(
  487. keccak256(
  488. "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract,bytes32 salt)"
  489. ),
  490. keccak256(abi.encodePacked(name())),
  491. keccak256(abi.encodePacked(_eip712DomainVersion())),
  492. block.chainid,
  493. address(this),
  494. keccak256(abi.encodePacked(chainId(), nativeContract()))
  495. )
  496. ),
  497. "_state.cachedDomainSeparator != expected"
  498. );
  499. require(
  500. _buildDomainSeparator(
  501. _eip712DomainNameHashed(),
  502. _eip712DomainSalt()
  503. ) ==
  504. keccak256(
  505. abi.encode(
  506. keccak256(
  507. "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract,bytes32 salt)"
  508. ),
  509. keccak256(abi.encodePacked(name())),
  510. keccak256(abi.encodePacked(_eip712DomainVersion())),
  511. block.chainid,
  512. address(this),
  513. keccak256(abi.encodePacked(chainId(), nativeContract()))
  514. )
  515. ),
  516. "_buildDomainSeparator() != expected"
  517. );
  518. require(
  519. _state.cachedThis == address(this),
  520. "_state.cachedThis != expected"
  521. );
  522. }
  523. function testPermitForPreviouslyDeployedImplementation(
  524. bytes32 walletPrivateKey,
  525. uint256 amount,
  526. address spender
  527. ) public {
  528. vm.assume(walletPrivateKey != bytes32(0));
  529. vm.assume(uint256(walletPrivateKey) < SECP256K1_CURVE_ORDER);
  530. vm.assume(spender != address(0));
  531. // initialize TokenImplementation as if it were the old implementation
  532. setupTestEnvironmentWithOldInitialize();
  533. require(_state.cachedSalt == bytes32(0), "cachedSalt is set");
  534. // prepare signer allowing for tokens to be spent
  535. uint256 deadline = 10;
  536. SignatureSetup memory signature = simulatePermitSignature(
  537. walletPrivateKey,
  538. spender,
  539. amount,
  540. deadline
  541. );
  542. // set allowance with permit
  543. permit(
  544. signature.allower,
  545. spender,
  546. amount,
  547. deadline,
  548. signature.v,
  549. signature.r,
  550. signature.s
  551. );
  552. require(
  553. allowance(signature.allower, spender) == amount,
  554. "allowance incorrect"
  555. );
  556. }
  557. // used to prevent stack too deep in test
  558. struct Eip712DomainOutput {
  559. bytes1 fields;
  560. string name;
  561. string version;
  562. uint256 chainId;
  563. address verifyingContract;
  564. bytes32 salt;
  565. uint256[] extensions;
  566. }
  567. function testPermitUsingEip712DomainValues(
  568. bytes32 walletPrivateKey,
  569. uint256 amount,
  570. address spender
  571. ) public {
  572. vm.assume(walletPrivateKey != bytes32(0));
  573. vm.assume(uint256(walletPrivateKey) < SECP256K1_CURVE_ORDER);
  574. vm.assume(spender != address(0));
  575. // initialize TokenImplementation
  576. setupTestEnvironmentWithInitialize();
  577. Eip712DomainOutput memory domain;
  578. (
  579. domain.fields,
  580. domain.name,
  581. domain.version,
  582. domain.chainId,
  583. domain.verifyingContract,
  584. domain.salt,
  585. domain.extensions
  586. ) = eip712Domain();
  587. require(domain.fields == hex"1F", "domainFields != expected");
  588. require(
  589. keccak256(abi.encodePacked(domain.name)) ==
  590. keccak256(abi.encodePacked(name())),
  591. "domainName != expected"
  592. );
  593. require(
  594. keccak256(abi.encodePacked(domain.name)) ==
  595. _eip712DomainNameHashed(),
  596. "domainName != _eip712DomainNameHashed()"
  597. );
  598. require(
  599. keccak256(abi.encodePacked(domain.version)) ==
  600. keccak256(abi.encodePacked("1")),
  601. "domainVersion != expected"
  602. );
  603. require(
  604. keccak256(abi.encodePacked(domain.version)) ==
  605. keccak256(abi.encodePacked(_eip712DomainVersion())),
  606. "domainVersion != _eip712DomainVersion()"
  607. );
  608. require(domain.chainId == block.chainid, "domainFields != expected");
  609. require(
  610. domain.chainId == _state.cachedChainId,
  611. "domainFields != _state.cachedChainId"
  612. );
  613. require(
  614. domain.verifyingContract == address(this),
  615. "domainVerifyingContract != expected"
  616. );
  617. require(
  618. domain.verifyingContract == _state.cachedThis,
  619. "domainVerifyingContract != _state.cachedThis"
  620. );
  621. require(
  622. domain.salt ==
  623. keccak256(abi.encodePacked(chainId(), nativeContract())),
  624. "domainFields != expected"
  625. );
  626. require(
  627. domain.salt == _eip712DomainSalt(),
  628. "domainFields != _eip712DomainSalt()"
  629. );
  630. require(
  631. domain.salt == _state.cachedSalt,
  632. "domainFields != _state.cachedSalt"
  633. );
  634. require(domain.extensions.length == 0, "domainExtensions.length != 0");
  635. // prepare signer allowing for tokens to be spent
  636. SignatureSetup memory signature;
  637. uint256 sk = uint256(walletPrivateKey);
  638. signature.allower = vm.addr(sk);
  639. uint256 deadline = 10;
  640. bytes32 structHash = keccak256(
  641. abi.encode(
  642. keccak256(
  643. "Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"
  644. ),
  645. signature.allower,
  646. spender,
  647. amount,
  648. nonces(signature.allower),
  649. deadline
  650. )
  651. );
  652. // build domain separator by hand using eip712Domain() output
  653. bytes32 domainSeparator = keccak256(
  654. abi.encode(
  655. keccak256(
  656. "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract,bytes32 salt)"
  657. ),
  658. keccak256(abi.encodePacked(domain.name)),
  659. keccak256(abi.encodePacked(domain.version)),
  660. domain.chainId,
  661. domain.verifyingContract,
  662. domain.salt
  663. )
  664. );
  665. // sign and set allowance with permit
  666. (signature.v, signature.r, signature.s) = vm.sign(
  667. sk,
  668. ECDSA.toTypedDataHash(domainSeparator, structHash)
  669. );
  670. permit(
  671. signature.allower,
  672. spender,
  673. amount,
  674. deadline,
  675. signature.v,
  676. signature.r,
  677. signature.s
  678. );
  679. require(
  680. allowance(signature.allower, spender) == amount,
  681. "allowance incorrect"
  682. );
  683. }
  684. function testPermitAfterUpdateDetails(
  685. bytes32 walletPrivateKey,
  686. uint256 amount,
  687. address spender,
  688. string calldata newName
  689. ) public {
  690. vm.assume(walletPrivateKey != bytes32(0));
  691. vm.assume(uint256(walletPrivateKey) < SECP256K1_CURVE_ORDER);
  692. vm.assume(spender != address(0));
  693. vm.assume(bytes(newName).length <= 32);
  694. // initialize TokenImplementation
  695. setupTestEnvironmentWithInitialize();
  696. string memory oldName = name();
  697. bytes32 oldDomainSeparator = _state.cachedDomainSeparator;
  698. // permit before updateDetails
  699. {
  700. uint256 deadline = 10;
  701. SignatureSetup memory signature = simulatePermitSignature(
  702. walletPrivateKey,
  703. spender,
  704. amount,
  705. deadline
  706. );
  707. // set allowance with permit
  708. permit(
  709. signature.allower,
  710. spender,
  711. amount,
  712. deadline,
  713. signature.v,
  714. signature.r,
  715. signature.s
  716. );
  717. require(
  718. allowance(signature.allower, spender) == amount,
  719. "allowance incorrect"
  720. );
  721. // revoke allowance to prep for next test
  722. _approve(signature.allower, spender, 0);
  723. }
  724. // asset metadata updated here
  725. updateDetails(
  726. newName,
  727. "NEW", // new symbol
  728. _state.metaLastUpdatedSequence + 1 // new sequence
  729. );
  730. require(
  731. keccak256(abi.encodePacked(newName)) !=
  732. keccak256(abi.encodePacked(oldName)),
  733. "newName == oldName"
  734. );
  735. require(
  736. _domainSeparatorV4() != oldDomainSeparator,
  737. "_domainSeparatorV4() == oldDomainSeparator"
  738. );
  739. require(
  740. _state.cachedDomainSeparator != oldDomainSeparator,
  741. "_state.cachedDomainSeparator == oldDomainSeparator"
  742. );
  743. require(
  744. _state.cachedDomainSeparator == _domainSeparatorV4(),
  745. "_state.cachedDomainSeparator != _domainSeparatorV4()"
  746. );
  747. // permit after updateDetails
  748. {
  749. uint256 deadline = 10;
  750. SignatureSetup memory signature = simulatePermitSignature(
  751. walletPrivateKey,
  752. spender,
  753. amount,
  754. deadline
  755. );
  756. // set allowance with permit
  757. permit(
  758. signature.allower,
  759. spender,
  760. amount,
  761. deadline,
  762. signature.v,
  763. signature.r,
  764. signature.s
  765. );
  766. require(
  767. allowance(signature.allower, spender) == amount,
  768. "allowance incorrect"
  769. );
  770. }
  771. }
  772. function testPermitForOldSalt(
  773. bytes32 walletPrivateKey,
  774. uint256 amount,
  775. address spender
  776. ) public {
  777. vm.assume(walletPrivateKey != bytes32(0));
  778. vm.assume(uint256(walletPrivateKey) < SECP256K1_CURVE_ORDER);
  779. vm.assume(spender != address(0));
  780. // initialize TokenImplementation
  781. setupTestEnvironmentWithInitialize();
  782. // hijack salt
  783. _state.cachedSalt = keccak256(abi.encodePacked("definitely not right"));
  784. require(
  785. _state.cachedSalt != _eip712DomainSalt(),
  786. "_state.cachedSalt == _eip712DomainSalt()"
  787. );
  788. // prepare signer allowing for tokens to be spent
  789. uint256 deadline = 10;
  790. SignatureSetup memory signature = simulatePermitSignature(
  791. walletPrivateKey,
  792. spender,
  793. amount,
  794. deadline
  795. );
  796. // set allowance with permit
  797. permit(
  798. signature.allower,
  799. spender,
  800. amount,
  801. deadline,
  802. signature.v,
  803. signature.r,
  804. signature.s
  805. );
  806. // verify salt is correct
  807. require(
  808. _state.cachedSalt == _eip712DomainSalt(),
  809. "_state.cachedSalt != _eip712DomainSalt()"
  810. );
  811. // then allowance
  812. require(
  813. allowance(signature.allower, spender) == amount,
  814. "allowance incorrect"
  815. );
  816. }
  817. function testPermitForOldName(
  818. bytes32 walletPrivateKey,
  819. uint256 amount,
  820. address spender
  821. ) public {
  822. vm.assume(walletPrivateKey != bytes32(0));
  823. vm.assume(uint256(walletPrivateKey) < SECP256K1_CURVE_ORDER);
  824. vm.assume(spender != address(0));
  825. // initialize TokenImplementation
  826. setupTestEnvironmentWithInitialize();
  827. // hijack name
  828. _state.cachedHashedName = keccak256("definitely not right");
  829. require(
  830. _state.cachedHashedName != _eip712DomainNameHashed(),
  831. "_state.cachedHashedName == _eip712DomainNameHashed()"
  832. );
  833. // prepare signer allowing for tokens to be spent
  834. uint256 deadline = 10;
  835. SignatureSetup memory signature = simulatePermitSignature(
  836. walletPrivateKey,
  837. spender,
  838. amount,
  839. deadline
  840. );
  841. // set allowance with permit
  842. permit(
  843. signature.allower,
  844. spender,
  845. amount,
  846. deadline,
  847. signature.v,
  848. signature.r,
  849. signature.s
  850. );
  851. // verify name is correct
  852. require(
  853. _state.cachedHashedName == _eip712DomainNameHashed(),
  854. "_state.cachedHashedName != _eip712DomainNameHashed()"
  855. );
  856. // then allowance
  857. require(
  858. allowance(signature.allower, spender) == amount,
  859. "allowance incorrect"
  860. );
  861. }
  862. }