Clones.test.js 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687
  1. const { ethers } = require('hardhat');
  2. const { expect } = require('chai');
  3. const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers');
  4. const shouldBehaveLikeClone = require('./Clones.behaviour');
  5. async function fixture() {
  6. const [deployer] = await ethers.getSigners();
  7. const factory = await ethers.deployContract('$Clones');
  8. const implementation = await ethers.deployContract('DummyImplementation');
  9. const newClone = async (initData, opts = {}) => {
  10. const clone = await factory.$clone.staticCall(implementation).then(address => implementation.attach(address));
  11. await factory.$clone(implementation);
  12. await deployer.sendTransaction({ to: clone, value: opts.value ?? 0n, data: initData ?? '0x' });
  13. return clone;
  14. };
  15. const newCloneDeterministic = async (initData, opts = {}) => {
  16. const salt = opts.salt ?? ethers.randomBytes(32);
  17. const clone = await factory.$cloneDeterministic
  18. .staticCall(implementation, salt)
  19. .then(address => implementation.attach(address));
  20. await factory.$cloneDeterministic(implementation, salt);
  21. await deployer.sendTransaction({ to: clone, value: opts.value ?? 0n, data: initData ?? '0x' });
  22. return clone;
  23. };
  24. return { deployer, factory, implementation, newClone, newCloneDeterministic };
  25. }
  26. describe('Clones', function () {
  27. beforeEach(async function () {
  28. Object.assign(this, await loadFixture(fixture));
  29. });
  30. describe('clone', function () {
  31. beforeEach(async function () {
  32. this.createClone = this.newClone;
  33. });
  34. shouldBehaveLikeClone();
  35. });
  36. describe('cloneDeterministic', function () {
  37. beforeEach(async function () {
  38. this.createClone = this.newCloneDeterministic;
  39. });
  40. shouldBehaveLikeClone();
  41. it('revert if address already used', async function () {
  42. const salt = ethers.randomBytes(32);
  43. // deploy once
  44. await expect(this.factory.$cloneDeterministic(this.implementation, salt)).to.emit(
  45. this.factory,
  46. 'return$cloneDeterministic',
  47. );
  48. // deploy twice
  49. await expect(this.factory.$cloneDeterministic(this.implementation, salt)).to.be.revertedWithCustomError(
  50. this.factory,
  51. 'ERC1167FailedCreateClone',
  52. );
  53. });
  54. it('address prediction', async function () {
  55. const salt = ethers.randomBytes(32);
  56. const creationCode = ethers.concat([
  57. '0x3d602d80600a3d3981f3363d3d373d3d3d363d73',
  58. this.implementation.target,
  59. '0x5af43d82803e903d91602b57fd5bf3',
  60. ]);
  61. const predicted = await this.factory.$predictDeterministicAddress(this.implementation, salt);
  62. const expected = ethers.getCreate2Address(this.factory.target, salt, ethers.keccak256(creationCode));
  63. expect(predicted).to.equal(expected);
  64. await expect(this.factory.$cloneDeterministic(this.implementation, salt))
  65. .to.emit(this.factory, 'return$cloneDeterministic')
  66. .withArgs(predicted);
  67. });
  68. });
  69. });