AccessManaged.test.js 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146
  1. const { ethers } = require('hardhat');
  2. const { expect } = require('chai');
  3. const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers');
  4. const { impersonate } = require('../../helpers/account');
  5. const time = require('../../helpers/time');
  6. async function fixture() {
  7. const [admin, roleMember, other] = await ethers.getSigners();
  8. const authority = await ethers.deployContract('$AccessManager', [admin]);
  9. const managed = await ethers.deployContract('$AccessManagedTarget', [authority]);
  10. const anotherAuthority = await ethers.deployContract('$AccessManager', [admin]);
  11. const authorityObserveIsConsuming = await ethers.deployContract('$AuthorityObserveIsConsuming');
  12. await impersonate(authority.target);
  13. const authorityAsSigner = await ethers.getSigner(authority.target);
  14. return {
  15. roleMember,
  16. other,
  17. authorityAsSigner,
  18. authority,
  19. managed,
  20. authorityObserveIsConsuming,
  21. anotherAuthority,
  22. };
  23. }
  24. describe('AccessManaged', function () {
  25. beforeEach(async function () {
  26. Object.assign(this, await loadFixture(fixture));
  27. });
  28. it('sets authority and emits AuthorityUpdated event during construction', async function () {
  29. await expect(this.managed.deploymentTransaction())
  30. .to.emit(this.managed, 'AuthorityUpdated')
  31. .withArgs(this.authority);
  32. });
  33. describe('restricted modifier', function () {
  34. beforeEach(async function () {
  35. this.selector = this.managed.fnRestricted.getFragment().selector;
  36. this.role = 42n;
  37. await this.authority.$_setTargetFunctionRole(this.managed, this.selector, this.role);
  38. await this.authority.$_grantRole(this.role, this.roleMember, 0, 0);
  39. });
  40. it('succeeds when role is granted without execution delay', async function () {
  41. await this.managed.connect(this.roleMember)[this.selector]();
  42. });
  43. it('reverts when role is not granted', async function () {
  44. await expect(this.managed.connect(this.other)[this.selector]())
  45. .to.be.revertedWithCustomError(this.managed, 'AccessManagedUnauthorized')
  46. .withArgs(this.other);
  47. });
  48. it('panics in short calldata', async function () {
  49. // We avoid adding the `restricted` modifier to the fallback function because other tests may depend on it
  50. // being accessible without restrictions. We check for the internal `_checkCanCall` instead.
  51. await expect(this.managed.$_checkCanCall(this.roleMember, '0x1234')).to.be.reverted;
  52. });
  53. describe('when role is granted with execution delay', function () {
  54. beforeEach(async function () {
  55. const executionDelay = 911n;
  56. await this.authority.$_grantRole(this.role, this.roleMember, 0, executionDelay);
  57. });
  58. it('reverts if the operation is not scheduled', async function () {
  59. const fn = this.managed.interface.getFunction(this.selector);
  60. const calldata = this.managed.interface.encodeFunctionData(fn, []);
  61. const opId = await this.authority.hashOperation(this.roleMember, this.managed, calldata);
  62. await expect(this.managed.connect(this.roleMember)[this.selector]())
  63. .to.be.revertedWithCustomError(this.authority, 'AccessManagerNotScheduled')
  64. .withArgs(opId);
  65. });
  66. it('succeeds if the operation is scheduled', async function () {
  67. // Arguments
  68. const delay = time.duration.hours(12);
  69. const fn = this.managed.interface.getFunction(this.selector);
  70. const calldata = this.managed.interface.encodeFunctionData(fn, []);
  71. // Schedule
  72. const scheduledAt = (await time.clock.timestamp()) + 1n;
  73. const when = scheduledAt + delay;
  74. await time.increaseTo.timestamp(scheduledAt, false);
  75. await this.authority.connect(this.roleMember).schedule(this.managed, calldata, when);
  76. // Set execution date
  77. await time.increaseTo.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);
  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);
  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);
  98. expect(await this.managed.authority()).to.equal(this.anotherAuthority);
  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,
  115. this.managed.interface.encodeFunctionData(fnRestricted, []),
  116. isConsumingScheduledOp.selector,
  117. );
  118. });
  119. });
  120. });