Nonces.behavior.js 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189
  1. const { ethers } = require('hardhat');
  2. const { expect } = require('chai');
  3. function shouldBehaveLikeNonces() {
  4. describe('should behave like Nonces', function () {
  5. const sender = ethers.Wallet.createRandom();
  6. const other = ethers.Wallet.createRandom();
  7. it('gets a nonce', async function () {
  8. expect(this.mock.nonces(sender)).to.eventually.equal(0n);
  9. });
  10. describe('_useNonce', function () {
  11. it('increments a nonce', async function () {
  12. expect(this.mock.nonces(sender)).to.eventually.equal(0n);
  13. const eventName = ['return$_useNonce', 'return$_useNonce_address'].find(name =>
  14. this.mock.interface.getEvent(name),
  15. );
  16. await expect(this.mock.$_useNonce(sender)).to.emit(this.mock, eventName).withArgs(0n);
  17. await expect(this.mock.nonces(sender)).to.eventually.equal(1n);
  18. });
  19. it("increments only sender's nonce", async function () {
  20. await expect(this.mock.nonces(sender)).to.eventually.equal(0n);
  21. await expect(this.mock.nonces(other)).to.eventually.equal(0n);
  22. await this.mock.$_useNonce(sender);
  23. await expect(this.mock.nonces(sender)).to.eventually.equal(1n);
  24. await expect(this.mock.nonces(other)).to.eventually.equal(0n);
  25. });
  26. });
  27. describe('_useCheckedNonce', function () {
  28. it('increments a nonce', async function () {
  29. // current nonce is 0n
  30. await expect(this.mock.nonces(sender)).to.eventually.equal(0n);
  31. await this.mock.$_useCheckedNonce(sender, 0n);
  32. await expect(this.mock.nonces(sender)).to.eventually.equal(1n);
  33. });
  34. it("increments only sender's nonce", async function () {
  35. // current nonce is 0n
  36. await expect(this.mock.nonces(sender)).to.eventually.equal(0n);
  37. await expect(this.mock.nonces(other)).to.eventually.equal(0n);
  38. await this.mock.$_useCheckedNonce(sender, 0n);
  39. await expect(this.mock.nonces(sender)).to.eventually.equal(1n);
  40. await expect(this.mock.nonces(other)).to.eventually.equal(0n);
  41. });
  42. it('reverts when nonce is not the expected', async function () {
  43. const currentNonce = await this.mock.nonces(sender);
  44. await expect(this.mock.$_useCheckedNonce(sender, currentNonce + 1n))
  45. .to.be.revertedWithCustomError(this.mock, 'InvalidAccountNonce')
  46. .withArgs(sender, currentNonce);
  47. });
  48. });
  49. });
  50. }
  51. function shouldBehaveLikeNoncesKeyed() {
  52. describe('should support nonces with keys', function () {
  53. const sender = ethers.Wallet.createRandom();
  54. const keyOffset = key => key << 64n;
  55. it('gets a nonce', async function () {
  56. await expect(this.mock.nonces(sender, ethers.Typed.uint192(0n))).to.eventually.equal(keyOffset(0n) + 0n);
  57. await expect(this.mock.nonces(sender, ethers.Typed.uint192(17n))).to.eventually.equal(keyOffset(17n) + 0n);
  58. });
  59. describe('_useNonce', function () {
  60. it('default variant uses key 0', async function () {
  61. await expect(this.mock.nonces(sender, ethers.Typed.uint192(0n))).to.eventually.equal(keyOffset(0n) + 0n);
  62. await expect(this.mock.nonces(sender, ethers.Typed.uint192(17n))).to.eventually.equal(keyOffset(17n) + 0n);
  63. await expect(this.mock.$_useNonce(sender)).to.emit(this.mock, 'return$_useNonce_address').withArgs(0n);
  64. await expect(this.mock.$_useNonce(sender, ethers.Typed.uint192(0n)))
  65. .to.emit(this.mock, 'return$_useNonce_address_uint192')
  66. .withArgs(keyOffset(0n) + 1n);
  67. await expect(this.mock.nonces(sender, ethers.Typed.uint192(0n))).to.eventually.equal(keyOffset(0n) + 2n);
  68. await expect(this.mock.nonces(sender, ethers.Typed.uint192(17n))).to.eventually.equal(keyOffset(17n) + 0n);
  69. });
  70. it('use nonce at another key', async function () {
  71. await expect(this.mock.nonces(sender, ethers.Typed.uint192(0n))).to.eventually.equal(keyOffset(0n) + 0n);
  72. await expect(this.mock.nonces(sender, ethers.Typed.uint192(17n))).to.eventually.equal(keyOffset(17n) + 0n);
  73. await expect(this.mock.$_useNonce(sender, ethers.Typed.uint192(17n)))
  74. .to.emit(this.mock, 'return$_useNonce_address_uint192')
  75. .withArgs(keyOffset(17n) + 0n);
  76. await expect(this.mock.$_useNonce(sender, ethers.Typed.uint192(17n)))
  77. .to.emit(this.mock, 'return$_useNonce_address_uint192')
  78. .withArgs(keyOffset(17n) + 1n);
  79. await expect(this.mock.nonces(sender, ethers.Typed.uint192(0n))).to.eventually.equal(keyOffset(0n) + 0n);
  80. await expect(this.mock.nonces(sender, ethers.Typed.uint192(17n))).to.eventually.equal(keyOffset(17n) + 2n);
  81. });
  82. });
  83. describe('_useCheckedNonce(address, uint256)', function () {
  84. it('default variant uses key 0', async function () {
  85. const currentNonce = await this.mock.nonces(sender, ethers.Typed.uint192(0n));
  86. await this.mock.$_useCheckedNonce(sender, currentNonce);
  87. await expect(this.mock.nonces(sender, ethers.Typed.uint192(0n))).to.eventually.equal(currentNonce + 1n);
  88. });
  89. it('use nonce at another key', async function () {
  90. const currentNonce = await this.mock.nonces(sender, ethers.Typed.uint192(17n));
  91. await this.mock.$_useCheckedNonce(sender, currentNonce);
  92. await expect(this.mock.nonces(sender, ethers.Typed.uint192(17n))).to.eventually.equal(currentNonce + 1n);
  93. });
  94. it('reverts when nonce is not the expected', async function () {
  95. const currentNonce = await this.mock.nonces(sender, ethers.Typed.uint192(42n));
  96. // use and increment
  97. await this.mock.$_useCheckedNonce(sender, currentNonce);
  98. // reuse same nonce
  99. await expect(this.mock.$_useCheckedNonce(sender, currentNonce))
  100. .to.be.revertedWithCustomError(this.mock, 'InvalidAccountNonce')
  101. .withArgs(sender, currentNonce + 1n);
  102. // use "future" nonce too early
  103. await expect(this.mock.$_useCheckedNonce(sender, currentNonce + 10n))
  104. .to.be.revertedWithCustomError(this.mock, 'InvalidAccountNonce')
  105. .withArgs(sender, currentNonce + 1n);
  106. });
  107. });
  108. describe('_useCheckedNonce(address, uint192, uint64)', function () {
  109. const MASK = 0xffffffffffffffffn;
  110. it('default variant uses key 0', async function () {
  111. const currentNonce = await this.mock.nonces(sender, ethers.Typed.uint192(0n));
  112. await this.mock.$_useCheckedNonce(sender, ethers.Typed.uint192(0n), currentNonce);
  113. await expect(this.mock.nonces(sender, ethers.Typed.uint192(0n))).to.eventually.equal(currentNonce + 1n);
  114. });
  115. it('use nonce at another key', async function () {
  116. const currentNonce = await this.mock.nonces(sender, ethers.Typed.uint192(17n));
  117. await this.mock.$_useCheckedNonce(sender, ethers.Typed.uint192(17n), currentNonce & MASK);
  118. await expect(this.mock.nonces(sender, ethers.Typed.uint192(17n))).to.eventually.equal(currentNonce + 1n);
  119. });
  120. it('reverts when nonce is not the expected', async function () {
  121. const currentNonce = await this.mock.nonces(sender, ethers.Typed.uint192(42n));
  122. // use and increment
  123. await this.mock.$_useCheckedNonce(sender, ethers.Typed.uint192(42n), currentNonce & MASK);
  124. // reuse same nonce
  125. await expect(this.mock.$_useCheckedNonce(sender, ethers.Typed.uint192(42n), currentNonce & MASK))
  126. .to.be.revertedWithCustomError(this.mock, 'InvalidAccountNonce')
  127. .withArgs(sender, currentNonce + 1n);
  128. // use "future" nonce too early
  129. await expect(this.mock.$_useCheckedNonce(sender, ethers.Typed.uint192(42n), (currentNonce & MASK) + 10n))
  130. .to.be.revertedWithCustomError(this.mock, 'InvalidAccountNonce')
  131. .withArgs(sender, currentNonce + 1n);
  132. });
  133. });
  134. });
  135. }
  136. module.exports = {
  137. shouldBehaveLikeNonces,
  138. shouldBehaveLikeNoncesKeyed,
  139. };