AccessManaged.test.js 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145
  1. const { bigint: time } = require('../../helpers/time');
  2. const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers');
  3. const { impersonate } = require('../../helpers/account');
  4. const { ethers } = require('hardhat');
  5. async function fixture() {
  6. const [admin, roleMember, other] = await ethers.getSigners();
  7. const authority = await ethers.deployContract('$AccessManager', [admin]);
  8. const managed = await ethers.deployContract('$AccessManagedTarget', [authority]);
  9. const anotherAuthority = await ethers.deployContract('$AccessManager', [admin]);
  10. const authorityObserveIsConsuming = await ethers.deployContract('$AuthorityObserveIsConsuming');
  11. await impersonate(authority.target);
  12. const authorityAsSigner = await ethers.getSigner(authority.target);
  13. return {
  14. roleMember,
  15. other,
  16. authorityAsSigner,
  17. authority,
  18. managed,
  19. authorityObserveIsConsuming,
  20. anotherAuthority,
  21. };
  22. }
  23. describe('AccessManaged', function () {
  24. beforeEach(async function () {
  25. Object.assign(this, await loadFixture(fixture));
  26. });
  27. it('sets authority and emits AuthorityUpdated event during construction', async function () {
  28. await expect(this.managed.deploymentTransaction())
  29. .to.emit(this.managed, 'AuthorityUpdated')
  30. .withArgs(this.authority.target);
  31. });
  32. describe('restricted modifier', function () {
  33. beforeEach(async function () {
  34. this.selector = this.managed.fnRestricted.getFragment().selector;
  35. this.role = 42n;
  36. await this.authority.$_setTargetFunctionRole(this.managed, this.selector, this.role);
  37. await this.authority.$_grantRole(this.role, this.roleMember, 0, 0);
  38. });
  39. it('succeeds when role is granted without execution delay', async function () {
  40. await this.managed.connect(this.roleMember)[this.selector]();
  41. });
  42. it('reverts when role is not granted', async function () {
  43. await expect(this.managed.connect(this.other)[this.selector]())
  44. .to.be.revertedWithCustomError(this.managed, 'AccessManagedUnauthorized')
  45. .withArgs(this.other.address);
  46. });
  47. it('panics in short calldata', async function () {
  48. // We avoid adding the `restricted` modifier to the fallback function because other tests may depend on it
  49. // being accessible without restrictions. We check for the internal `_checkCanCall` instead.
  50. await expect(this.managed.$_checkCanCall(this.roleMember, '0x1234')).to.be.reverted;
  51. });
  52. describe('when role is granted with execution delay', function () {
  53. beforeEach(async function () {
  54. const executionDelay = 911n;
  55. await this.authority.$_grantRole(this.role, this.roleMember, 0, executionDelay);
  56. });
  57. it('reverts if the operation is not scheduled', async function () {
  58. const fn = this.managed.interface.getFunction(this.selector);
  59. const calldata = this.managed.interface.encodeFunctionData(fn, []);
  60. const opId = await this.authority.hashOperation(this.roleMember, this.managed, calldata);
  61. await expect(this.managed.connect(this.roleMember)[this.selector]())
  62. .to.be.revertedWithCustomError(this.authority, 'AccessManagerNotScheduled')
  63. .withArgs(opId);
  64. });
  65. it('succeeds if the operation is scheduled', async function () {
  66. // Arguments
  67. const delay = time.duration.hours(12);
  68. const fn = this.managed.interface.getFunction(this.selector);
  69. const calldata = this.managed.interface.encodeFunctionData(fn, []);
  70. // Schedule
  71. const timestamp = await time.clock.timestamp();
  72. const scheduledAt = timestamp + 1n;
  73. const when = scheduledAt + delay;
  74. await time.forward.timestamp(scheduledAt, false);
  75. await this.authority.connect(this.roleMember).schedule(this.managed, calldata, when);
  76. // Set execution date
  77. await time.forward.timestamp(when, false);
  78. // Shouldn't revert
  79. await this.managed.connect(this.roleMember)[this.selector]();
  80. });
  81. });
  82. });
  83. describe('setAuthority', function () {
  84. it('reverts if the caller is not the authority', async function () {
  85. await expect(this.managed.connect(this.other).setAuthority(this.other))
  86. .to.be.revertedWithCustomError(this.managed, 'AccessManagedUnauthorized')
  87. .withArgs(this.other.address);
  88. });
  89. it('reverts if the new authority is not a valid authority', async function () {
  90. await expect(this.managed.connect(this.authorityAsSigner).setAuthority(this.other))
  91. .to.be.revertedWithCustomError(this.managed, 'AccessManagedInvalidAuthority')
  92. .withArgs(this.other.address);
  93. });
  94. it('sets authority and emits AuthorityUpdated event', async function () {
  95. await expect(this.managed.connect(this.authorityAsSigner).setAuthority(this.anotherAuthority))
  96. .to.emit(this.managed, 'AuthorityUpdated')
  97. .withArgs(this.anotherAuthority.target);
  98. expect(await this.managed.authority()).to.equal(this.anotherAuthority.target);
  99. });
  100. });
  101. describe('isConsumingScheduledOp', function () {
  102. beforeEach(async function () {
  103. await this.managed.connect(this.authorityAsSigner).setAuthority(this.authorityObserveIsConsuming);
  104. });
  105. it('returns bytes4(0) when not consuming operation', async function () {
  106. expect(await this.managed.isConsumingScheduledOp()).to.equal('0x00000000');
  107. });
  108. it('returns isConsumingScheduledOp selector when consuming operation', async function () {
  109. const isConsumingScheduledOp = this.managed.interface.getFunction('isConsumingScheduledOp()');
  110. const fnRestricted = this.managed.fnRestricted.getFragment();
  111. await expect(this.managed.connect(this.other).fnRestricted())
  112. .to.emit(this.authorityObserveIsConsuming, 'ConsumeScheduledOpCalled')
  113. .withArgs(
  114. this.other.address,
  115. this.managed.interface.encodeFunctionData(fnRestricted, []),
  116. isConsumingScheduledOp.selector,
  117. );
  118. });
  119. });
  120. });