Add ability to create clones with initial value in Clones.sol (#4936)
Co-authored-by: Hadrien Croubois <hadrien.croubois@gmail.com> Co-authored-by: ernestognw <ernestognw@gmail.com>
This commit is contained in:
@ -13,6 +13,25 @@ module.exports = function shouldBehaveLikeClone() {
|
||||
});
|
||||
};
|
||||
|
||||
describe('construct with value', function () {
|
||||
const value = 10n;
|
||||
|
||||
it('factory has enough balance', async function () {
|
||||
await this.deployer.sendTransaction({ to: this.factory, value });
|
||||
|
||||
const instance = await this.createClone({ deployValue: value });
|
||||
await expect(instance.deploymentTransaction()).to.changeEtherBalances([this.factory, instance], [-value, value]);
|
||||
|
||||
expect(await ethers.provider.getBalance(instance)).to.equal(value);
|
||||
});
|
||||
|
||||
it('factory does not have enough balance', async function () {
|
||||
await expect(this.createClone({ deployValue: value }))
|
||||
.to.be.revertedWithCustomError(this.factory, 'InsufficientBalance')
|
||||
.withArgs(0n, value);
|
||||
});
|
||||
});
|
||||
|
||||
describe('initialization without parameters', function () {
|
||||
describe('non payable', function () {
|
||||
const expectedInitializedValue = 10n;
|
||||
@ -23,7 +42,7 @@ module.exports = function shouldBehaveLikeClone() {
|
||||
|
||||
describe('when not sending balance', function () {
|
||||
beforeEach('creating proxy', async function () {
|
||||
this.proxy = await this.createClone(this.initializeData);
|
||||
this.proxy = await this.createClone({ initData: this.initializeData });
|
||||
});
|
||||
|
||||
assertProxyInitialization({
|
||||
@ -36,7 +55,7 @@ module.exports = function shouldBehaveLikeClone() {
|
||||
const value = 10n ** 6n;
|
||||
|
||||
it('reverts', async function () {
|
||||
await expect(this.createClone(this.initializeData, { value })).to.be.reverted;
|
||||
await expect(this.createClone({ initData: this.initializeData, initValue: value })).to.be.reverted;
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -50,7 +69,7 @@ module.exports = function shouldBehaveLikeClone() {
|
||||
|
||||
describe('when not sending balance', function () {
|
||||
beforeEach('creating proxy', async function () {
|
||||
this.proxy = await this.createClone(this.initializeData);
|
||||
this.proxy = await this.createClone({ initData: this.initializeData });
|
||||
});
|
||||
|
||||
assertProxyInitialization({
|
||||
@ -63,7 +82,7 @@ module.exports = function shouldBehaveLikeClone() {
|
||||
const value = 10n ** 6n;
|
||||
|
||||
beforeEach('creating proxy', async function () {
|
||||
this.proxy = await this.createClone(this.initializeData, { value });
|
||||
this.proxy = await this.createClone({ initData: this.initializeData, initValue: value });
|
||||
});
|
||||
|
||||
assertProxyInitialization({
|
||||
@ -86,7 +105,7 @@ module.exports = function shouldBehaveLikeClone() {
|
||||
|
||||
describe('when not sending balance', function () {
|
||||
beforeEach('creating proxy', async function () {
|
||||
this.proxy = await this.createClone(this.initializeData);
|
||||
this.proxy = await this.createClone({ initData: this.initializeData });
|
||||
});
|
||||
|
||||
assertProxyInitialization({
|
||||
@ -99,7 +118,7 @@ module.exports = function shouldBehaveLikeClone() {
|
||||
const value = 10n ** 6n;
|
||||
|
||||
it('reverts', async function () {
|
||||
await expect(this.createClone(this.initializeData, { value })).to.be.reverted;
|
||||
await expect(this.createClone({ initData: this.initializeData, initValue: value })).to.be.reverted;
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -115,7 +134,7 @@ module.exports = function shouldBehaveLikeClone() {
|
||||
|
||||
describe('when not sending balance', function () {
|
||||
beforeEach('creating proxy', async function () {
|
||||
this.proxy = await this.createClone(this.initializeData);
|
||||
this.proxy = await this.createClone({ initData: this.initializeData });
|
||||
});
|
||||
|
||||
assertProxyInitialization({
|
||||
@ -128,7 +147,7 @@ module.exports = function shouldBehaveLikeClone() {
|
||||
const value = 10n ** 6n;
|
||||
|
||||
beforeEach('creating proxy', async function () {
|
||||
this.proxy = await this.createClone(this.initializeData, { value });
|
||||
this.proxy = await this.createClone({ initData: this.initializeData, initValue: value });
|
||||
});
|
||||
|
||||
assertProxyInitialization({
|
||||
|
||||
@ -10,21 +10,29 @@ async function fixture() {
|
||||
const factory = await ethers.deployContract('$Clones');
|
||||
const implementation = await ethers.deployContract('DummyImplementation');
|
||||
|
||||
const newClone = async (initData, opts = {}) => {
|
||||
const newClone = async (opts = {}) => {
|
||||
const clone = await factory.$clone.staticCall(implementation).then(address => implementation.attach(address));
|
||||
await factory.$clone(implementation);
|
||||
await deployer.sendTransaction({ to: clone, value: opts.value ?? 0n, data: initData ?? '0x' });
|
||||
return clone;
|
||||
const tx = await (opts.deployValue
|
||||
? factory.$clone(implementation, ethers.Typed.uint256(opts.deployValue))
|
||||
: factory.$clone(implementation));
|
||||
if (opts.initData || opts.initValue) {
|
||||
await deployer.sendTransaction({ to: clone, value: opts.initValue ?? 0n, data: opts.initData ?? '0x' });
|
||||
}
|
||||
return Object.assign(clone, { deploymentTransaction: () => tx });
|
||||
};
|
||||
|
||||
const newCloneDeterministic = async (initData, opts = {}) => {
|
||||
const newCloneDeterministic = async (opts = {}) => {
|
||||
const salt = opts.salt ?? ethers.randomBytes(32);
|
||||
const clone = await factory.$cloneDeterministic
|
||||
.staticCall(implementation, salt)
|
||||
.then(address => implementation.attach(address));
|
||||
await factory.$cloneDeterministic(implementation, salt);
|
||||
await deployer.sendTransaction({ to: clone, value: opts.value ?? 0n, data: initData ?? '0x' });
|
||||
return clone;
|
||||
const tx = await (opts.deployValue
|
||||
? factory.$cloneDeterministic(implementation, salt, ethers.Typed.uint256(opts.deployValue))
|
||||
: factory.$cloneDeterministic(implementation, salt));
|
||||
if (opts.initData || opts.initValue) {
|
||||
await deployer.sendTransaction({ to: clone, value: opts.initValue ?? 0n, data: opts.initData ?? '0x' });
|
||||
}
|
||||
return Object.assign(clone, { deploymentTransaction: () => tx });
|
||||
};
|
||||
|
||||
return { deployer, factory, implementation, newClone, newCloneDeterministic };
|
||||
@ -56,13 +64,13 @@ describe('Clones', function () {
|
||||
// deploy once
|
||||
await expect(this.factory.$cloneDeterministic(this.implementation, salt)).to.emit(
|
||||
this.factory,
|
||||
'return$cloneDeterministic',
|
||||
'return$cloneDeterministic_address_bytes32',
|
||||
);
|
||||
|
||||
// deploy twice
|
||||
await expect(this.factory.$cloneDeterministic(this.implementation, salt)).to.be.revertedWithCustomError(
|
||||
this.factory,
|
||||
'ERC1167FailedCreateClone',
|
||||
'FailedDeployment',
|
||||
);
|
||||
});
|
||||
|
||||
@ -80,7 +88,7 @@ describe('Clones', function () {
|
||||
expect(predicted).to.equal(expected);
|
||||
|
||||
await expect(this.factory.$cloneDeterministic(this.implementation, salt))
|
||||
.to.emit(this.factory, 'return$cloneDeterministic')
|
||||
.to.emit(this.factory, 'return$cloneDeterministic_address_bytes32')
|
||||
.withArgs(predicted);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user