Clones.test.js 3.4 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495
  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 (opts = {}) => {
  10. const clone = await factory.$clone.staticCall(implementation).then(address => implementation.attach(address));
  11. const tx = await (opts.deployValue
  12. ? factory.$clone(implementation, ethers.Typed.uint256(opts.deployValue))
  13. : factory.$clone(implementation));
  14. if (opts.initData || opts.initValue) {
  15. await deployer.sendTransaction({ to: clone, value: opts.initValue ?? 0n, data: opts.initData ?? '0x' });
  16. }
  17. return Object.assign(clone, { deploymentTransaction: () => tx });
  18. };
  19. const newCloneDeterministic = async (opts = {}) => {
  20. const salt = opts.salt ?? ethers.randomBytes(32);
  21. const clone = await factory.$cloneDeterministic
  22. .staticCall(implementation, salt)
  23. .then(address => implementation.attach(address));
  24. const tx = await (opts.deployValue
  25. ? factory.$cloneDeterministic(implementation, salt, ethers.Typed.uint256(opts.deployValue))
  26. : factory.$cloneDeterministic(implementation, salt));
  27. if (opts.initData || opts.initValue) {
  28. await deployer.sendTransaction({ to: clone, value: opts.initValue ?? 0n, data: opts.initData ?? '0x' });
  29. }
  30. return Object.assign(clone, { deploymentTransaction: () => tx });
  31. };
  32. return { deployer, factory, implementation, newClone, newCloneDeterministic };
  33. }
  34. describe('Clones', function () {
  35. beforeEach(async function () {
  36. Object.assign(this, await loadFixture(fixture));
  37. });
  38. describe('clone', function () {
  39. beforeEach(async function () {
  40. this.createClone = this.newClone;
  41. });
  42. shouldBehaveLikeClone();
  43. });
  44. describe('cloneDeterministic', function () {
  45. beforeEach(async function () {
  46. this.createClone = this.newCloneDeterministic;
  47. });
  48. shouldBehaveLikeClone();
  49. it('revert if address already used', async function () {
  50. const salt = ethers.randomBytes(32);
  51. // deploy once
  52. await expect(this.factory.$cloneDeterministic(this.implementation, salt)).to.emit(
  53. this.factory,
  54. 'return$cloneDeterministic_address_bytes32',
  55. );
  56. // deploy twice
  57. await expect(this.factory.$cloneDeterministic(this.implementation, salt)).to.be.revertedWithCustomError(
  58. this.factory,
  59. 'FailedDeployment',
  60. );
  61. });
  62. it('address prediction', async function () {
  63. const salt = ethers.randomBytes(32);
  64. const creationCode = ethers.concat([
  65. '0x3d602d80600a3d3981f3363d3d373d3d3d363d73',
  66. this.implementation.target,
  67. '0x5af43d82803e903d91602b57fd5bf3',
  68. ]);
  69. const predicted = await this.factory.$predictDeterministicAddress(this.implementation, salt);
  70. const expected = ethers.getCreate2Address(this.factory.target, salt, ethers.keccak256(creationCode));
  71. expect(predicted).to.equal(expected);
  72. await expect(this.factory.$cloneDeterministic(this.implementation, salt))
  73. .to.emit(this.factory, 'return$cloneDeterministic_address_bytes32')
  74. .withArgs(predicted);
  75. });
  76. });
  77. });