UNPKG

@etherspot/contracts

Version:

Etherspot Solidity contracts

360 lines (359 loc) 19.5 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const hardhat_1 = require("hardhat"); const ethers_1 = require("ethers"); const shared_1 = require("../shared"); const { getSigners, provider } = hardhat_1.ethers; describe('PersonalAccountRegistryV1', () => { let signers; let personalAccountImplementation; let personalAccountRegistry; let wrappedWeiToken; before(async () => { signers = await getSigners(); personalAccountImplementation = await (0, shared_1.deployContract)('PersonalAccountImplementationV1'); personalAccountRegistry = await (0, shared_1.deployContract)('PersonalAccountRegistry'); wrappedWeiToken = await (0, shared_1.deployContract)('WrappedWeiToken'); await (0, shared_1.processTx)(personalAccountImplementation.initialize(personalAccountRegistry.address)); await (0, shared_1.processTx)(personalAccountRegistry.initialize([], personalAccountImplementation.address, (0, shared_1.randomAddress)())); }); context('deployAccount()', () => { let saltOwner; let account; before(async () => { saltOwner = signers.pop(); account = await personalAccountRegistry.computeAccountAddress(saltOwner.address); await (0, shared_1.processTx)(personalAccountRegistry .connect(saltOwner) .addAccountOwner(account, (0, shared_1.randomAddress)())); }); it('expect to deploy account', async () => { const { events } = await (0, shared_1.processTx)(personalAccountRegistry.connect(saltOwner).deployAccount(account)); expect(events[0].event).toBe('AccountDeployed'); expect(events[0].args.account).toBe(account); }); }); context('addAccountOwner()', () => { let saltOwner; let owner; let account; before(async () => { saltOwner = signers.pop(); owner = signers.pop(); account = await personalAccountRegistry.computeAccountAddress(saltOwner.address); }); it('expect to account owner by salt owner', async () => { const { events } = await (0, shared_1.processTx)(personalAccountRegistry .connect(saltOwner) .addAccountOwner(account, owner.address)); expect(events[0].event).toBe('AccountOwnerAdded'); expect(events[0].args.account).toBe(account); expect(events[0].args.owner).toBe(saltOwner.address); expect(events[1].event).toBe('AccountOwnerAdded'); expect(events[1].args.account).toBe(account); expect(events[1].args.owner).toBe(owner.address); }); it('expect to account owner by owner', async () => { const newOwner = (0, shared_1.randomAddress)(); const { events: [event], } = await (0, shared_1.processTx)(personalAccountRegistry .connect(owner) .addAccountOwner(account, newOwner)); expect(event.event).toBe('AccountOwnerAdded'); expect(event.args.account).toBe(account); expect(event.args.owner).toBe(newOwner); }); it('expect to revert when owner exists', async () => { await expect(personalAccountRegistry .connect(saltOwner) .addAccountOwner(account, owner.address)).rejects.toThrow(/revert/); }); it('expect to revert when sender is not an owner exists', async () => { await expect(personalAccountRegistry .connect(signers.pop()) .addAccountOwner(account, (0, shared_1.randomAddress)())).rejects.toThrow(/revert/); }); }); context('removeAccountOwner()', () => { let saltOwner; let owner1; let owner2; let account; before(async () => { saltOwner = signers.pop(); owner1 = signers.pop(); owner2 = signers.pop(); account = await personalAccountRegistry.computeAccountAddress(saltOwner.address); await (0, shared_1.processTx)(personalAccountRegistry .connect(saltOwner) .addAccountOwner(account, owner1.address)); await (0, shared_1.processTx)(personalAccountRegistry .connect(saltOwner) .addAccountOwner(account, owner2.address)); }); it('expect to remove account owner', async () => { const { events: [event], } = await (0, shared_1.processTx)(personalAccountRegistry .connect(saltOwner) .removeAccountOwner(account, owner1.address)); expect(event.event).toBe('AccountOwnerRemoved'); expect(event.args.account).toBe(account); expect(event.args.owner).toBe(owner1.address); }); it("expect to revert when owner doesn't exist", async () => { await expect(personalAccountRegistry .connect(saltOwner) .removeAccountOwner(account, (0, shared_1.randomAddress)())).rejects.toThrow(/revert/); }); it('expect to revert when sender is not an owner exists', async () => { await expect(personalAccountRegistry .connect(signers.pop()) .removeAccountOwner(account, owner2.address)).rejects.toThrow(/revert/); }); }); context('executeAccountTransaction()', () => { let owner; let account; let balance = 1000; before(async () => { owner = signers.pop(); account = await personalAccountRegistry.computeAccountAddress(owner.address); await (0, shared_1.processTx)(owner.sendTransaction({ to: account, value: balance, })); }); it('expect to execute first transaction', async () => { const to = (0, shared_1.randomAddress)(); const value = 100; const data = '0x'; const { events } = await (0, shared_1.processTx)(personalAccountRegistry .connect(owner) .executeAccountTransaction(account, to, value, data)); balance -= value; expect(events[0].event).toBe('AccountOwnerAdded'); expect(events[0].args.account).toBe(account); expect(events[0].args.owner).toBe(owner.address); expect(events[1].event).toBe('AccountDeployed'); expect(events[1].args.account).toBe(account); expect(events[2].event).toBe('AccountTransactionExecuted'); expect(events[2].args.account).toBe(account); expect(events[2].args.to).toBe(to); expect(events[2].args.value).toBeBN(value); expect(events[2].args.data).toBe(data); await expect(provider.getBalance(account)).resolves.toBeBN(balance); }); it('expect to execute second transaction (with data)', async () => { const to = (0, shared_1.randomAddress)(); const value = 200; const data = '0x010203'; const { events: [event], } = await (0, shared_1.processTx)(personalAccountRegistry .connect(owner) .executeAccountTransaction(account, to, value, data)); balance -= value; expect(event.event).toBe('AccountTransactionExecuted'); expect(event.args.account).toBe(account); expect(event.args.to).toBe(to); expect(event.args.value).toBeBN(value); expect(event.args.data).toBe(data); await expect(provider.getBalance(account)).resolves.toBeBN(balance); }); it('expect to revert when sender is not an account owner', async () => { const to = (0, shared_1.randomAddress)(); const value = 200; const data = '0x010203'; await expect(personalAccountRegistry .connect(owner) .executeAccountTransaction((0, shared_1.randomAddress)(), to, value, data)).rejects.toThrow(/revert/); }); }); context('refundAccountCall()', () => { let owner; let account; before(async () => { owner = signers.pop(); account = await personalAccountRegistry.computeAccountAddress(owner.address); }); context('# with wei', () => { let balance = 1000; before(async () => { await (0, shared_1.processTx)(owner.sendTransaction({ to: account, value: balance, })); }); it('expect to do first refund', async () => { const value = 100; const ownerBalance = await owner.getBalance(); const { events, totalCost } = await (0, shared_1.processTx)(personalAccountRegistry .connect(owner) .refundAccountCall(account, ethers_1.constants.AddressZero, value)); balance -= value; expect(events[0].event).toBe('AccountOwnerAdded'); expect(events[1].event).toBe('AccountDeployed'); expect(events[2].event).toBe('AccountCallRefunded'); expect(events[2].args.account).toBe(account); expect(events[2].args.beneficiary).toBe(owner.address); expect(events[2].args.token).toBeZeroAddress(); expect(events[2].args.value).toBeBN(value); await expect(owner.getBalance()).resolves.toBeBN(ownerBalance.sub(totalCost).add(value)); await expect(provider.getBalance(account)).resolves.toBeBN(balance); }); it('expect to do second refund', async () => { const value = 50; const ownerBalance = await owner.getBalance(); const { events: [event], totalCost, } = await (0, shared_1.processTx)(personalAccountRegistry .connect(owner) .refundAccountCall(account, ethers_1.constants.AddressZero, value)); balance -= value; expect(event.event).toBe('AccountCallRefunded'); expect(event.args.account).toBe(account); expect(event.args.beneficiary).toBe(owner.address); expect(event.args.token).toBeZeroAddress(); expect(event.args.value).toBeBN(value); await expect(owner.getBalance()).resolves.toBeBN(ownerBalance.sub(totalCost).add(value)); await expect(provider.getBalance(account)).resolves.toBeBN(balance); }); }); context('# with token', () => { let balance = 1000; before(async () => { await (0, shared_1.processTx)(wrappedWeiToken.connect(owner).depositTo(account, { value: balance, })); }); it('expect to do first refund', async () => { const value = 100; const ownerBalance = await wrappedWeiToken.balanceOf(owner.address); const { events } = await (0, shared_1.processTx)(personalAccountRegistry .connect(owner) .refundAccountCall(account, wrappedWeiToken.address, value)); balance -= value; const event = events.find(({ event }) => event === 'AccountCallRefunded'); expect(event.args.account).toBe(account); expect(event.args.beneficiary).toBe(owner.address); expect(event.args.token).toBe(wrappedWeiToken.address); expect(event.args.value).toBeBN(value); await expect(wrappedWeiToken.balanceOf(owner.address)).resolves.toBeBN(ownerBalance.add(value)); await expect(wrappedWeiToken.balanceOf(account)).resolves.toBeBN(balance); }); it('expect to do second refund', async () => { const value = 50; const ownerBalance = await wrappedWeiToken.balanceOf(owner.address); const { events } = await (0, shared_1.processTx)(personalAccountRegistry .connect(owner) .refundAccountCall(account, wrappedWeiToken.address, value)); balance -= value; const event = events.find(({ event }) => event === 'AccountCallRefunded'); expect(event.event).toBe('AccountCallRefunded'); expect(event.args.account).toBe(account); expect(event.args.beneficiary).toBe(owner.address); expect(event.args.token).toBe(wrappedWeiToken.address); expect(event.args.value).toBeBN(value); await expect(wrappedWeiToken.balanceOf(owner.address)).resolves.toBeBN(ownerBalance.add(value)); await expect(wrappedWeiToken.balanceOf(account)).resolves.toBeBN(balance); }); }); }); context('computeAccountAddress()', () => { it('expect to return correct account address', async () => { const owner = (0, shared_1.randomAddress)(); const account = await (0, shared_1.computeAccountAddress)(personalAccountRegistry, 'Account', owner, personalAccountRegistry.address, personalAccountImplementation.address); await expect(personalAccountRegistry.computeAccountAddress(owner)).resolves.toBe(account); }); }); context('isAccountDeployed()', () => { let account; before(async () => { const owner = signers.pop(); account = await personalAccountRegistry.computeAccountAddress(owner.address); await (0, shared_1.processTx)(personalAccountRegistry .connect(owner) .executeAccountTransaction(account, (0, shared_1.randomAddress)(), 0, '0x010203')); }); it('expect to return true when account is deployed', async () => { await expect(personalAccountRegistry.isAccountDeployed(account)).resolves.toBeTruthy(); }); it('expect to return false when account is not deployed', async () => { await expect(personalAccountRegistry.isAccountDeployed((0, shared_1.randomAddress)())).resolves.toBeFalsy(); }); }); context('verifyAccountOwner()', () => { const addedOwner = (0, shared_1.randomAddress)(); const removedOwner = (0, shared_1.randomAddress)(); let saltOwner; let account; before(async () => { saltOwner = signers.pop(); personalAccountRegistry = personalAccountRegistry.connect(saltOwner); account = await personalAccountRegistry.computeAccountAddress(saltOwner.address); await (0, shared_1.processTx)(personalAccountRegistry .connect(saltOwner) .addAccountOwner(account, addedOwner)); await (0, shared_1.processTx)(personalAccountRegistry.addAccountOwner(account, removedOwner)); await (0, shared_1.processTx)(personalAccountRegistry.removeAccountOwner(account, removedOwner)); }); it('expect to return true for salt owner', async () => { await expect(personalAccountRegistry.verifyAccountOwner(account, saltOwner.address)).resolves.toBeTruthy(); }); it('expect to return true for added owner', async () => { await expect(personalAccountRegistry.verifyAccountOwner(account, addedOwner)).resolves.toBeTruthy(); }); it('expect to return false for removed owner', async () => { await expect(personalAccountRegistry.verifyAccountOwner(account, removedOwner)).resolves.toBeFalsy(); }); it('expect to return false for unknown owner', async () => { await expect(personalAccountRegistry.verifyAccountOwner(account, (0, shared_1.randomAddress)())).resolves.toBeFalsy(); }); }); context('verifyAccountOwnerAtBlock()', () => { const addedOwner = (0, shared_1.randomAddress)(); const removedOwner = (0, shared_1.randomAddress)(); let saltOwner; let account; let removedAt; before(async () => { saltOwner = signers.pop(); personalAccountRegistry = personalAccountRegistry.connect(saltOwner); account = await personalAccountRegistry.computeAccountAddress(saltOwner.address); await (0, shared_1.processTx)(personalAccountRegistry.addAccountOwner(account, addedOwner)); await (0, shared_1.processTx)(personalAccountRegistry.addAccountOwner(account, removedOwner)); ({ blockNumber: removedAt } = await (0, shared_1.processTx)(personalAccountRegistry.removeAccountOwner(account, removedOwner))); }); it('expect to return true for salt owner at any block', async () => { await expect(personalAccountRegistry.verifyAccountOwnerAtBlock(account, saltOwner.address, removedAt)).resolves.toBeTruthy(); }); it('expect to return true for added owner at any block', async () => { await expect(personalAccountRegistry.verifyAccountOwnerAtBlock(account, addedOwner, removedAt)).resolves.toBeTruthy(); }); it('expect to return true for removed owner before removing block', async () => { await expect(personalAccountRegistry.verifyAccountOwnerAtBlock(account, removedOwner, removedAt - 1)).resolves.toBeTruthy(); }); it('expect to return false for removed owner after removing block', async () => { await expect(personalAccountRegistry.verifyAccountOwnerAtBlock(account, removedOwner, removedAt)).resolves.toBeFalsy(); }); it('expect to return false for unknown owner at any block', async () => { await expect(personalAccountRegistry.verifyAccountOwnerAtBlock(account, (0, shared_1.randomAddress)(), removedAt)).resolves.toBeFalsy(); }); }); context('isValidAccountSignature()', () => { let saltOwner; let unknownOwner; let account; before(async () => { saltOwner = signers.pop(); unknownOwner = signers.pop(); account = await personalAccountRegistry.computeAccountAddress(saltOwner.address); }); it('expect to return true for valid signature', async () => { const message = 'test message'; const signature = await saltOwner.signMessage(message); await expect(personalAccountRegistry['isValidAccountSignature(address,bytes32,bytes)'](account, ethers_1.utils.hashMessage(message), signature)).resolves.toBeTruthy(); await expect(personalAccountRegistry['isValidAccountSignature(address,bytes,bytes)'](account, ethers_1.utils.hexlify(ethers_1.utils.toUtf8Bytes(message)), signature)).resolves.toBeTruthy(); }); it('expect to return false for invalid signature', async () => { const message = 'test message'; const signature = await unknownOwner.signMessage(message); await expect(personalAccountRegistry['isValidAccountSignature(address,bytes32,bytes)'](account, ethers_1.utils.hashMessage(message), signature)).resolves.toBeFalsy(); await expect(personalAccountRegistry['isValidAccountSignature(address,bytes,bytes)'](account, ethers_1.utils.hexlify(ethers_1.utils.toUtf8Bytes(message)), signature)).resolves.toBeFalsy(); }); }); });