diff --git a/governance/scripts/deployments/serializer.js b/governance/scripts/deployments/serializer.js deleted file mode 100644 index f9684e22a4f..00000000000 --- a/governance/scripts/deployments/serializer.js +++ /dev/null @@ -1,26 +0,0 @@ -const { ethers } = require('hardhat') - -async function main() { - const LockSerializer = await ethers.getContractFactory('LockSerializer') - const serializer = await LockSerializer.deploy() - await serializer.deployed() - - // eslint-disable-next-line no-console - console.log( - `SERIALIZER > deployed to : ${serializer.address} (tx: ${serializer.deployTransaction.hash})` - ) - - return serializer.address -} - -// execute as standalone -if (require.main === module) { - main() - .then(() => process.exit(0)) - .catch((error) => { - console.error(error) - process.exit(1) - }) -} - -module.exports = main diff --git a/governance/tasks/deploy.js b/governance/tasks/deploy.js index 3d9fa819d12..a3839265794 100644 --- a/governance/tasks/deploy.js +++ b/governance/tasks/deploy.js @@ -117,12 +117,6 @@ task('deploy:template', 'Deploy PublicLock contract') return await templateDeployer({ publicLockVersion }) }) -task('deploy:serializer', 'Deploy LockSerializer').setAction(async () => { - // eslint-disable-next-line global-require - const serializerDeployer = require('../scripts/deployments/serializer') - return await serializerDeployer() -}) - task('deploy:governor', 'Deploy governor contracts') .addParam('upAddress', 'address of the ERC20 token to use for governance') .addOptionalParam('timelockAddress', 'address of a timelock contract') diff --git a/governance/test/scripts/deploy/lock.js b/governance/test/scripts/deploy/lock.js index d0dd2bdeccc..f0fc94e61c6 100644 --- a/governance/test/scripts/deploy/lock.js +++ b/governance/test/scripts/deploy/lock.js @@ -1,152 +1,139 @@ -const { ethers } = require('hardhat') -const { UnlockV9 } = require('@unlock-protocol/contracts') -const { PublicLockV8, LockSerializer } = require('@unlock-protocol/contracts') +const { upgrades, ethers } = require('hardhat') +const { Unlock, PublicLock } = require('@unlock-protocol/contracts') const assert = require('assert') const { lockFixtures: Locks, ADDRESS_ZERO, getEvent, + getLock, + deployUpgradeableContract, + copyAndBuildContractsAtVersion, } = require('@unlock-protocol/hardhat-helpers') + const deployLock = require('../../../scripts/deployments/lock') -const compareValues = async (serialized, lock) => { - const arrays = [ - 'keyOwners', - 'expirationTimestamps', - 'keyManagers', - 'tokenURISample', +const getDeployArgs = async ({ name }) => { + const [, , manager] = await ethers.getSigners() + const deployArgs = { + owner: await manager.getAddress(), + duration: Locks[name].expirationDuration, + tokenAddress: ADDRESS_ZERO, + price: Locks[name].keyPrice.toString(), + maxNumberOfKeys: Locks[name].maxNumberOfKeys, + name: Locks[name].lockName, + } + return deployArgs +} + +const createLock = async ({ name, unlock, version = 14 }) => { + const deployArgs = await getDeployArgs({ name }) + + // encode initializer data + const iface = new ethers.Interface(PublicLock.abi) + const fragment = iface.getFunction( + 'initialize(address,uint256,address,uint256,uint256,string)' + ) + const calldata = iface.encodeFunctionData(fragment, Object.values(deployArgs)) + + // parse result + const tx = await unlock.createUpgradeableLockAtVersion(calldata, version) + const receipt = await tx.wait() + const { args } = await getEvent(receipt, 'NewLock') + + return { + name, + deployArgs, + lockAddress: args.newLockAddress, + } +} + +const fetchLock = async (lockAddress) => { + const lock = await getLock(lockAddress) + const methods = [ + 'expirationDuration', + 'keyPrice', + 'maxNumberOfKeys', + 'freeTrialLength', + 'refundPenaltyBasisPoints', + 'transferFeeBasisPoints', + 'name', + 'symbol', + 'publicLockVersion', + 'tokenAddress', + 'numberOfOwners', + 'totalSupply', ] - const propNames = Object.keys(serialized) - .filter((k) => Number.isNaN(Number.parseInt(k))) // remove numbers from array index - .filter((k) => !arrays.includes(k)) // exclude arrays - const values = await Promise.all(propNames.map((k) => lock[k]())) - - // assertions - propNames.forEach((k, i) => { - if ( - ethers.BigNumber.isBigNumber(serialized[k]) && - ethers.BigNumber.isBigNumber(values[i]) - ) { - assert.equal( - serialized[k].eq(values[i]), - true, - `different serialized value ${k}, ${serialized[k]}, ${values[i]}` - ) - } else { - assert.equal( - serialized[k], - values[i], - `different serialized value ${k}, ${serialized[k]}, ${values[i]}` - ) - } - }) + const results = {} + await Promise.all(methods.map((method) => (results[method] = lock[method]()))) + + return results } describe('Scripts/deploy:lock', () => { - let serializer let unlockAddress - let PublicLock - const locks = {} - // addresses - let unlockOwner - let manager + const deployedLocks = {} - beforeEach(async () => { - ;[unlockOwner, , manager] = await ethers.getSigners() + before(async () => { + const [unlockOwner] = await ethers.getSigners() // deploy unlock - const Unlock = await ethers.getContractFactory( - UnlockV9.abi, - UnlockV9.bytecode + const [qualifiedPath] = await copyAndBuildContractsAtVersion( + `${__dirname}/..`, + [{ contractName: 'Unlock', version: 13 }] ) - const unlock = await Unlock.deploy() + ;({ address: unlockAddress } = await deployUpgradeableContract( + qualifiedPath, + [await unlockOwner.getAddress()], + { + initializer: 'initialize(address)', + } + )) + const unlock = await ethers.getContractAt(Unlock.abi, unlockAddress) // deploy template - PublicLock = await ethers.getContractFactory( - PublicLockV8.abi, - PublicLockV8.bytecode + const PublicLockFactory = await ethers.getContractFactory( + PublicLock.abi, + PublicLock.bytecode ) - const publicLock = await PublicLock.deploy() + const publicLock = await PublicLockFactory.deploy() // set unlock - await unlock.initialize(unlockOwner.address) - await unlock - .connect(unlockOwner) - .setLockTemplate(await publicLock.getAddress()) - - // deploy serializer - const LockSerializerFactory = await ethers.getContractFactory( - LockSerializer.abi, - LockSerializer.bytecode + await unlock.addLockTemplate( + await publicLock.getAddress(), + await publicLock.publicLockVersion() ) - serializer = await LockSerializerFactory.deploy() + await unlock.setLockTemplate(await publicLock.getAddress()) - // deploy locks + // deploy locks using local script await Promise.all( Object.keys(Locks) - .filter((name) => name != 'NON_EXPIRING') // avoid max 100yrs revert + // .filter((name) => name != 'NON_EXPIRING') // avoid max 100yrs revert .map(async (name) => { - const lockArgs = [ - Locks[name].expirationDuration, - ADDRESS_ZERO, - Locks[name].keyPrice.toString(), - Locks[name].maxNumberOfKeys, - Locks[name].lockName, - ethers.hexlify(ethers.randomBytes(12)), - ] - const tx = await unlock.createLock(...lockArgs) - const receipt = await tx.wait() - const { args } = await getEvent(receipt, 'NewLock') - locks[name] = await PublicLock.attach(args.newLockAddress) - locks[name].params = Locks[name] + deployedLocks[name] = await createLock({ name, unlock }) }) ) - - unlockAddress = await unlock.getAddress() }) - it('identical init args', async () => { - // check for all locks - Object.keys(locks).forEach(async (id) => { - const lock = locks[id] - const serialized = await serializer.serialize(lock.address) + it('identical initial settings after deployment', async () => { + Object.keys(deployedLocks).forEach(async (name) => { + const deployArgs = getDeployArgs({ name }) + + // make sure init are the same + assert.equal(deployArgs, deployedLocks[name].deployArgs) // redeploy our lock const newLockAddress = await deployLock({ unlockAddress, - unlockVersion: 8, - serializedLock: serialized, - salt: ethers.hexlify(ethers.randomBytes(12)), + unlockVersion: 14, + ...deployArgs, }) // make sure values are identical - const newLock = PublicLock.attach(newLockAddress) - await compareValues(serialized, lock) - await compareValues(serialized, newLock) - }) - }) - it('identical custom fees', async () => { - const lock = locks.FIRST - - // set custom values - await lock.addLockManager(manager.address) - await lock.connect(manager).updateRefundPenalty(2222, 2222) - await lock.connect(manager).updateTransferFee(2222) - - const serialized = await serializer.serialize(await lock.getAddress()) - - // redeploy our lock - const newLockAddress = await deployLock({ - unlockAddress, - unlockVersion: 8, - serializedLock: serialized, - salt: ethers.hexlify(ethers.randomBytes(12)), + assert.equal( + await fetchLock(deployedLocks[name].lockAddress), + await fetchLock(newLockAddress) + ) }) - - // make sure values are identical - const newLock = PublicLock.attach(newLockAddress) - await compareValues(serialized, lock) - await compareValues(serialized, newLock) }) }) diff --git a/packages/contracts/src/abis/utils/LockSerializer.json b/packages/contracts/src/abis/utils/LockSerializer.json deleted file mode 100644 index e4f9da31494..00000000000 --- a/packages/contracts/src/abis/utils/LockSerializer.json +++ /dev/null @@ -1,247 +0,0 @@ -{ - "_format": "hh-sol-artifact-1", - "contractName": "LockSerializer", - "sourceName": "contracts/utils/LockSerializer.sol", - "abi": [ - { - "inputs": [], - "stateMutability": "nonpayable", - "type": "constructor" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "internalType": "address", - "name": "newLockAddress", - "type": "address" - } - ], - "name": "LockCLoned", - "type": "event" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "lockAddress", - "type": "address" - } - ], - "name": "serialize", - "outputs": [ - { - "components": [ - { - "internalType": "uint256", - "name": "expirationDuration", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "keyPrice", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "maxNumberOfKeys", - "type": "uint256" - }, - { - "internalType": "address", - "name": "beneficiary", - "type": "address" - }, - { - "internalType": "uint256", - "name": "freeTrialLength", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "refundPenaltyBasisPoints", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "transferFeeBasisPoints", - "type": "uint256" - }, - { - "internalType": "string", - "name": "name", - "type": "string" - }, - { - "internalType": "string", - "name": "symbol", - "type": "string" - }, - { - "internalType": "string", - "name": "tokenURISample", - "type": "string" - }, - { - "internalType": "uint256", - "name": "publicLockVersion", - "type": "uint256" - }, - { - "internalType": "address", - "name": "tokenAddress", - "type": "address" - }, - { - "internalType": "uint256", - "name": "numberOfOwners", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "totalSupply", - "type": "uint256" - }, - { - "internalType": "address[]", - "name": "keyOwners", - "type": "address[]" - }, - { - "internalType": "address[]", - "name": "keyManagers", - "type": "address[]" - }, - { - "internalType": "uint256[]", - "name": "expirationTimestamps", - "type": "uint256[]" - } - ], - "internalType": "struct LockSerializer.Lock", - "name": "", - "type": "tuple" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "contract IPublicLockV8", - "name": "lock", - "type": "address" - } - ], - "name": "serializeFees", - "outputs": [ - { - "components": [ - { - "internalType": "uint256", - "name": "freeTrialLength", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "refundPenaltyBasisPoints", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "transferFeeBasisPoints", - "type": "uint256" - } - ], - "internalType": "struct LockSerializer.LockFees", - "name": "", - "type": "tuple" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "contract IPublicLockV8", - "name": "lock", - "type": "address" - } - ], - "name": "serializeMetadata", - "outputs": [ - { - "components": [ - { - "internalType": "string", - "name": "name", - "type": "string" - }, - { - "internalType": "string", - "name": "symbol", - "type": "string" - }, - { - "internalType": "string", - "name": "tokenURISample", - "type": "string" - } - ], - "internalType": "struct LockSerializer.LockMetadata", - "name": "", - "type": "tuple" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "contract IPublicLockV8", - "name": "lock", - "type": "address" - } - ], - "name": "serializePriceInfo", - "outputs": [ - { - "components": [ - { - "internalType": "uint256", - "name": "expirationDuration", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "keyPrice", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "maxNumberOfKeys", - "type": "uint256" - }, - { - "internalType": "address", - "name": "beneficiary", - "type": "address" - } - ], - "internalType": "struct LockSerializer.LockPriceInfo", - "name": "", - "type": "tuple" - } - ], - "stateMutability": "view", - "type": "function" - } - ], - "bytecode": "0x608060405234801561001057600080fd5b50611318806100206000396000f3fe608060405234801561001057600080fd5b506004361061004c5760003560e01c806347a277d9146100515780635d6da55b146100a9578063ccf8171a146100de578063eece4b1f146100fe575b600080fd5b61006461005f366004610eb9565b61011e565b6040516100a091908151815260208083015190820152604080830151908201526060918201516001600160a01b03169181019190915260800190565b60405180910390f35b6100bc6100b7366004610eb9565b610355565b60408051825181526020808401519082015291810151908201526060016100a0565b6100f16100ec366004610eb9565b6104f9565b6040516100a09190611077565b61011161010c366004610eb9565b610729565b6040516100a091906110d8565b610152604051806080016040528060008152602001600081526020016000815260200160006001600160a01b031681525090565b6000826001600160a01b03166311a4c03a6040518163ffffffff1660e01b815260040160206040518083038186803b15801561018d57600080fd5b505afa1580156101a1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906101c59190610fc1565b90506000836001600160a01b03166310e569736040518163ffffffff1660e01b815260040160206040518083038186803b15801561020257600080fd5b505afa158015610216573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061023a9190610fc1565b90506000846001600160a01b03166374b6c1066040518163ffffffff1660e01b815260040160206040518083038186803b15801561027757600080fd5b505afa15801561028b573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906102af9190610fc1565b90506000856001600160a01b03166338af3eed6040518163ffffffff1660e01b815260040160206040518083038186803b1580156102ec57600080fd5b505afa158015610300573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906103249190610edc565b60408051608081018252958652602086019490945292840191909152506001600160a01b0316606082015292915050565b61037960405180606001604052806000815260200160008152602001600081525090565b6000826001600160a01b031663a375cb056040518163ffffffff1660e01b815260040160206040518083038186803b1580156103b457600080fd5b505afa1580156103c8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906103ec9190610fc1565b90506000836001600160a01b03166356e0d51f6040518163ffffffff1660e01b815260040160206040518083038186803b15801561042957600080fd5b505afa15801561043d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104619190610fc1565b90506000846001600160a01b031663183767da6040518163ffffffff1660e01b815260040160206040518083038186803b15801561049e57600080fd5b505afa1580156104b2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104d69190610fc1565b604080516060810182529485526020850193909352918301919091525092915050565b61051d60405180606001604052806060815260200160608152602001606081525090565b6000826001600160a01b03166306fdde036040518163ffffffff1660e01b815260040160006040518083038186803b15801561055857600080fd5b505afa15801561056c573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526105949190810190610f18565b90506000836001600160a01b03166395d89b416040518163ffffffff1660e01b815260040160006040518083038186803b1580156105d157600080fd5b505afa1580156105e5573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261060d9190810190610f18565b90506000846001600160a01b03166318160ddd6040518163ffffffff1660e01b815260040160206040518083038186803b15801561064a57600080fd5b505afa15801561065e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106829190610fc1565b60405163c87b56dd60e01b8152600481018290529091506000906001600160a01b0387169063c87b56dd9060240160006040518083038186803b1580156106c857600080fd5b505afa1580156106dc573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526107049190810190610f18565b6040805160608101825295865260208601949094529284019290925250909392505050565b6107c260405180610220016040528060008152602001600081526020016000815260200160006001600160a01b031681526020016000815260200160008152602001600081526020016060815260200160608152602001606081526020016000815260200160006001600160a01b0316815260200160008152602001600081526020016060815260200160608152602001606081525090565b6000829050806001600160a01b0316634136aa356040518163ffffffff1660e01b815260040160206040518083038186803b15801561080057600080fd5b505afa158015610814573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108389190610ef8565b15156001146108995760405162461bcd60e51b815260206004820152602360248201527f44697361626c6564206c6f636b2063616e206e6f742062652073657269616c696044820152621e995960ea1b606482015260840160405180910390fd5b60006108a4826104f9565b905060006108b183610355565b905060006108be8461011e565b90506000846001600160a01b031663d1bbd49c6040518163ffffffff1660e01b815260040160206040518083038186803b1580156108fb57600080fd5b505afa15801561090f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109339190610fc1565b90506000856001600160a01b0316639d76ea586040518163ffffffff1660e01b815260040160206040518083038186803b15801561097057600080fd5b505afa158015610984573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109a89190610edc565b90506000866001600160a01b03166318160ddd6040518163ffffffff1660e01b815260040160206040518083038186803b1580156109e557600080fd5b505afa1580156109f9573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a1d9190610fc1565b90506000876001600160a01b03166393fd18446040518163ffffffff1660e01b815260040160206040518083038186803b158015610a5a57600080fd5b505afa158015610a6e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a929190610fc1565b905060008267ffffffffffffffff811115610abd57634e487b7160e01b600052604160045260246000fd5b604051908082528060200260200182016040528015610ae6578160200160208202803683370190505b50905060008367ffffffffffffffff811115610b1257634e487b7160e01b600052604160045260246000fd5b604051908082528060200260200182016040528015610b3b578160200160208202803683370190505b50905060008467ffffffffffffffff811115610b6757634e487b7160e01b600052604160045260246000fd5b604051908082528060200260200182016040528015610b90578160200160208202803683370190505b50905060005b85811015610e06576000610bab82600161123b565b6040516331a9108f60e11b8152600481018290529091506001600160a01b038e1690636352211e9060240160206040518083038186803b158015610bee57600080fd5b505afa158015610c02573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c269190610edc565b858381518110610c4657634e487b7160e01b600052603260045260246000fd5b60200260200101906001600160a01b031690816001600160a01b0316815250508c6001600160a01b031663abdf82ce868481518110610c9557634e487b7160e01b600052603260045260246000fd5b60200260200101516040518263ffffffff1660e01b8152600401610cc891906001600160a01b0391909116815260200190565b60206040518083038186803b158015610ce057600080fd5b505afa158015610cf4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d189190610fc1565b838381518110610d3857634e487b7160e01b600052603260045260246000fd5b6020908102919091010152604051634d025fed60e01b8152600481018290526001600160a01b038e1690634d025fed9060240160206040518083038186803b158015610d8357600080fd5b505afa158015610d97573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610dbb9190610edc565b848381518110610ddb57634e487b7160e01b600052603260045260246000fd5b6001600160a01b03909216602092830291909101909101525080610dfe81611283565b915050610b96565b506040805161022081018252895181526020808b0151818301528a8301518284015260609a8b01516001600160a01b039081169b83019b909b528b5160808301528b81015160a08301529a82015160c08201528b5160e0820152998b01516101008b01529990990151610120890152610140880195909552929094166101608601526101808501939093526101a08401929092526101c08301919091526101e08201526102008101919091529392505050565b600060208284031215610eca578081fd5b8135610ed5816112ca565b9392505050565b600060208284031215610eed578081fd5b8151610ed5816112ca565b600060208284031215610f09578081fd5b81518015158114610ed5578182fd5b600060208284031215610f29578081fd5b815167ffffffffffffffff80821115610f40578283fd5b818401915084601f830112610f53578283fd5b815181811115610f6557610f656112b4565b604051601f8201601f19908116603f01168101908382118183101715610f8d57610f8d6112b4565b81604052828152876020848701011115610fa5578586fd5b610fb6836020830160208801611253565b979650505050505050565b600060208284031215610fd2578081fd5b5051919050565b6000815180845260208085019450808401835b838110156110115781516001600160a01b031687529582019590820190600101610fec565b509495945050505050565b6000815180845260208085019450808401835b838110156110115781518752958201959082019060010161102f565b60008151808452611063816020860160208601611253565b601f01601f19169290920160200192915050565b602081526000825160606020840152611093608084018261104b565b90506020840151601f19808584030160408601526110b1838361104b565b92506040860151915080858403016060860152506110cf828261104b565b95945050505050565b602081528151602082015260208201516040820152604082015160608201526000606083015161111360808401826001600160a01b03169052565b50608083015160a083015260a083015160c083015260c083015160e083015260e0830151610220610100818186015261115061024086018461104b565b9250808601519050601f19610120818786030181880152611171858461104b565b945080880151925050610140818786030181880152611190858461104b565b908801516101608881019190915288015190945091506101806111bd818801846001600160a01b03169052565b8701516101a0878101919091528701516101c08088019190915287015186850382016101e0808901919091529092506111f68584610fd9565b9450808801519250506102008187860301818801526112158584610fd9565b908801518782039092018488015293509050611231838261101c565b9695505050505050565b6000821982111561124e5761124e61129e565b500190565b60005b8381101561126e578181015183820152602001611256565b8381111561127d576000848401525b50505050565b60006000198214156112975761129761129e565b5060010190565b634e487b7160e01b600052601160045260246000fd5b634e487b7160e01b600052604160045260246000fd5b6001600160a01b03811681146112df57600080fd5b5056fea2646970667358221220bd11855a09f377c5d0d161b6f2fd77830d556efef6da5ea2047c4488a6e6fe6064736f6c63430008040033", - "linkReferences": {}, - "deployedLinkReferences": {} -} diff --git a/packages/contracts/src/contracts/utils/LockSerializer.sol b/packages/contracts/src/contracts/utils/LockSerializer.sol deleted file mode 100644 index 2ce4b23784f..00000000000 --- a/packages/contracts/src/contracts/utils/LockSerializer.sol +++ /dev/null @@ -1,155 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.2; - -import "hardhat/console.sol"; -import "../PublicLock/IPublicLockV8sol8.sol"; - -contract LockSerializer { - constructor() {} - - event LockCLoned(address newLockAddress); - - struct Lock { - // priceInfo - uint expirationDuration; - uint keyPrice; - uint maxNumberOfKeys; - address beneficiary; - // fees - uint256 freeTrialLength; - uint256 refundPenaltyBasisPoints; - uint256 transferFeeBasisPoints; - // metadata - string name; - string symbol; - string tokenURISample; - // protocol - uint publicLockVersion; - address tokenAddress; - // ownerhsip - uint numberOfOwners; - uint256 totalSupply; - address[] keyOwners; - address[] keyManagers; - uint[] expirationTimestamps; - // address lockCreator; - } - - // We split in multiple structs to avoid "Stack To Deep" solc - struct LockPriceInfo { - // fees - uint expirationDuration; - uint keyPrice; - uint maxNumberOfKeys; - address beneficiary; - } - - struct LockFees { - // fees - uint256 freeTrialLength; - uint256 refundPenaltyBasisPoints; - uint256 transferFeeBasisPoints; - } - - struct LockMetadata { - // metadata - string name; - string symbol; - string tokenURISample; - } - - function serializePriceInfo( - IPublicLockV8 lock - ) public view returns (LockPriceInfo memory) { - uint expirationDuration = lock.expirationDuration(); - uint keyPrice = lock.keyPrice(); - uint maxNumberOfKeys = lock.maxNumberOfKeys(); - address beneficiary = lock.beneficiary(); - return - LockPriceInfo(expirationDuration, keyPrice, maxNumberOfKeys, beneficiary); - } - - function serializeFees( - IPublicLockV8 lock - ) public view returns (LockFees memory) { - uint256 freeTrialLength = lock.freeTrialLength(); - uint256 refundPenaltyBasisPoints = lock.refundPenaltyBasisPoints(); - uint256 transferFeeBasisPoints = lock.transferFeeBasisPoints(); - return - LockFees( - freeTrialLength, - refundPenaltyBasisPoints, - transferFeeBasisPoints - ); - } - - function serializeMetadata( - IPublicLockV8 lock - ) public view returns (LockMetadata memory) { - string memory name = lock.name(); - string memory symbol = lock.symbol(); - - // get the latest TokenURI to use as sample - uint totalSupply = lock.totalSupply(); - string memory tokenURISample = lock.tokenURI(totalSupply); - - return LockMetadata(name, symbol, tokenURISample); - } - - function serialize(address lockAddress) public view returns (Lock memory) { - IPublicLockV8 lock = IPublicLockV8(lockAddress); - require(lock.isAlive() == true, "Disabled lock can not be serialized"); - - LockMetadata memory metadata = serializeMetadata(lock); - LockFees memory fees = serializeFees(lock); - LockPriceInfo memory priceInfo = serializePriceInfo(lock); - - // protocol - uint publicLockVersion = lock.publicLockVersion(); - address tokenAddress = lock.tokenAddress(); - - // ownership - uint256 totalSupply = lock.totalSupply(); - uint numberOfOwners = lock.numberOfOwners(); - - // keys - address[] memory keyOwners = new address[](totalSupply); - address[] memory keyManagers = new address[](totalSupply); - uint[] memory expirationTimestamps = new uint[](totalSupply); - - // tokenId starts at 1, so totalSupply + 1 is needed - for (uint256 i = 0; i < totalSupply; i++) { - uint256 tokenId = i + 1; - keyOwners[i] = lock.ownerOf(tokenId); - expirationTimestamps[i] = lock.keyExpirationTimestampFor(keyOwners[i]); - keyManagers[i] = lock.keyManagerOf(tokenId); - } - - Lock memory serializedLock = Lock( - // keys - priceInfo.expirationDuration, - priceInfo.keyPrice, - priceInfo.maxNumberOfKeys, - priceInfo.beneficiary, - // fees - fees.freeTrialLength, - fees.refundPenaltyBasisPoints, - fees.transferFeeBasisPoints, - // metadata - metadata.name, - metadata.symbol, - metadata.tokenURISample, - // protocol - publicLockVersion, - tokenAddress, - // ownerhsip - numberOfOwners, - totalSupply, - keyOwners, - keyManagers, - expirationTimestamps - ); - - return serializedLock; - } -} diff --git a/smart-contracts/contracts/utils/LockSerializer.sol b/smart-contracts/contracts/utils/LockSerializer.sol deleted file mode 100644 index 0151d9c10df..00000000000 --- a/smart-contracts/contracts/utils/LockSerializer.sol +++ /dev/null @@ -1,148 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.2; - -import "../interfaces/IPublicLock.sol"; - -contract LockSerializer { - constructor() {} - - event LockCLoned(address newLockAddress); - - struct Lock { - // priceInfo - uint expirationDuration; - uint keyPrice; - uint maxNumberOfKeys; - // fees - uint256 freeTrialLength; - uint256 refundPenaltyBasisPoints; - uint256 transferFeeBasisPoints; - // metadata - string name; - string symbol; - string tokenURISample; - // protocol - uint publicLockVersion; - address tokenAddress; - // ownerhsip - uint numberOfOwners; - uint256 totalSupply; - address[] keyOwners; - address[] keyManagers; - uint[] expirationTimestamps; - // address lockCreator; - } - - // We split in multiple structs to avoid "Stack To Deep" solc - struct LockPriceInfo { - // fees - uint expirationDuration; - uint keyPrice; - uint maxNumberOfKeys; - } - - struct LockFees { - // fees - uint256 freeTrialLength; - uint256 refundPenaltyBasisPoints; - uint256 transferFeeBasisPoints; - } - - struct LockMetadata { - // metadata - string name; - string symbol; - string tokenURISample; - } - - function serializePriceInfo( - IPublicLock lock - ) public view returns (LockPriceInfo memory) { - uint expirationDuration = lock.expirationDuration(); - uint keyPrice = lock.keyPrice(); - uint maxNumberOfKeys = lock.maxNumberOfKeys(); - return LockPriceInfo(expirationDuration, keyPrice, maxNumberOfKeys); - } - - function serializeFees( - IPublicLock lock - ) public view returns (LockFees memory) { - uint256 freeTrialLength = lock.freeTrialLength(); - uint256 refundPenaltyBasisPoints = lock.refundPenaltyBasisPoints(); - uint256 transferFeeBasisPoints = lock.transferFeeBasisPoints(); - return - LockFees( - freeTrialLength, - refundPenaltyBasisPoints, - transferFeeBasisPoints - ); - } - - function serializeMetadata( - IPublicLock lock - ) public view returns (LockMetadata memory) { - string memory name = lock.name(); - string memory symbol = lock.symbol(); - - // get the latest TokenURI to use as sample - uint totalSupply = lock.totalSupply(); - string memory tokenURISample = lock.tokenURI(totalSupply); - - return LockMetadata(name, symbol, tokenURISample); - } - - function serialize(address lockAddress) public view returns (Lock memory) { - IPublicLock lock = IPublicLock(lockAddress); - - LockMetadata memory metadata = serializeMetadata(lock); - LockFees memory fees = serializeFees(lock); - LockPriceInfo memory priceInfo = serializePriceInfo(lock); - - // protocol - uint publicLockVersion = lock.publicLockVersion(); - address tokenAddress = lock.tokenAddress(); - - // ownership - uint256 totalSupply = lock.totalSupply(); - uint numberOfOwners = lock.numberOfOwners(); - - // keys - address[] memory keyOwners = new address[](totalSupply); - address[] memory keyManagers = new address[](totalSupply); - uint[] memory expirationTimestamps = new uint[](totalSupply); - - // tokenId starts at 1, so totalSupply + 1 is needed - for (uint256 i = 0; i < totalSupply; i++) { - uint256 tokenId = i + 1; - keyOwners[i] = lock.ownerOf(tokenId); - expirationTimestamps[i] = lock.keyExpirationTimestampFor(tokenId); - keyManagers[i] = lock.keyManagerOf(tokenId); - } - - Lock memory serializedLock = Lock( - // keys - priceInfo.expirationDuration, - priceInfo.keyPrice, - priceInfo.maxNumberOfKeys, - // fees - fees.freeTrialLength, - fees.refundPenaltyBasisPoints, - fees.transferFeeBasisPoints, - // metadata - metadata.name, - metadata.symbol, - metadata.tokenURISample, - // protocol - publicLockVersion, - tokenAddress, - // ownerhsip - numberOfOwners, - totalSupply, - keyOwners, - keyManagers, - expirationTimestamps - ); - - return serializedLock; - } -} diff --git a/smart-contracts/test/Lock/purchaseWithoutUnlock.js b/smart-contracts/test/Lock/purchaseWithoutUnlock.js index 003138e9910..891b7765e0c 100644 --- a/smart-contracts/test/Lock/purchaseWithoutUnlock.js +++ b/smart-contracts/test/Lock/purchaseWithoutUnlock.js @@ -11,7 +11,7 @@ const keyPrice = ethers.parseEther('0.01') const breakUnlock = async (unlockAddress) => { // deploy a random contract to break Unlock implementation - const BrokenUnlock = await ethers.getContractFactory('LockSerializer') + const BrokenUnlock = await ethers.getContractFactory('KeyManager') await upgrades.upgradeProxy(unlockAddress, BrokenUnlock, { unsafeSkipStorageCheck: true, }) diff --git a/smart-contracts/test/LockSerializer/_compareValues.js b/smart-contracts/test/LockSerializer/_compareValues.js deleted file mode 100644 index 4add0860f74..00000000000 --- a/smart-contracts/test/LockSerializer/_compareValues.js +++ /dev/null @@ -1,37 +0,0 @@ -const assert = require('assert') -const { ethers } = require('hardhat') - -const compareValues = async (serialized, lock) => { - const arrays = [ - 'keyOwners', - 'expirationTimestamps', - 'keyManagers', - 'tokenURISample', - ] - const propNames = Object.keys(serialized) - .filter((k) => Number.isNaN(Number.parseInt(k))) // remove numbers from array index - .filter((k) => !arrays.includes(k)) // exclude arrays - const values = await Promise.all(propNames.map((k) => lock[k]())) - - // assertions - propNames.forEach((k, i) => { - if ( - ethers.BigNumber.isBigNumber(serialized[k]) && - ethers.BigNumber.isBigNumber(values[i]) - ) { - assert.equal( - serialized[k] == values[i], - true, - `different serialized value ${k}, ${serialized[k]}, ${values[i]}` - ) - } else { - assert.equal( - serialized[k], - values[i], - `different serialized value ${k}, ${serialized[k]}, ${values[i]}` - ) - } - }) -} - -module.exports = compareValues diff --git a/smart-contracts/test/LockSerializer/serialize.js b/smart-contracts/test/LockSerializer/serialize.js deleted file mode 100644 index 33ec7d0c5ca..00000000000 --- a/smart-contracts/test/LockSerializer/serialize.js +++ /dev/null @@ -1,104 +0,0 @@ -const assert = require('assert') -const { ethers } = require('hardhat') -const compareValues = require('./_compareValues') - -const { deployLock, ADDRESS_ZERO } = require('../helpers') - -describe('LockSerializer', () => { - let lock - let serializer - let lockOwner - let keyOwner - - beforeEach(async () => { - ;[lockOwner, keyOwner] = await ethers.getSigners() - - // deploy serializer - const LockSerializer = await ethers.getContractFactory('LockSerializer') - serializer = await LockSerializer.deploy() - - // get locks (truffle version) - lock = await deployLock() - }) - - describe('serialize', () => { - it('deserialize values properly', async () => { - const serialized = await serializer.serialize(await lock.getAddress()) - await compareValues(serialized, lock) - }) - - it('fetch a sample of the tokenURI properly', async () => { - const keyPrice = ethers.parseEther('0.01') - const baseTokenURI = 'https://hahaha.com/' - - // purchase a key - const tx = await lock - .connect(keyOwner) - .purchase( - [keyPrice], - [await keyOwner.getAddress()], - [ADDRESS_ZERO], - [ADDRESS_ZERO], - ['0x'], - { value: keyPrice } - ) - await tx.wait() - - const totalSupply = await lock.totalSupply() - - // default URI - const serialized = await serializer.serialize(await lock.getAddress()) - await assert.equal( - serialized.tokenURISample, - `${(await lock.getAddress()).toLowerCase()}/${totalSupply}` - ) - - // custom URI - await lock - .connect(lockOwner) - .setLockMetadata(await lock.name(), await lock.symbol(), baseTokenURI) - const serializedCustomBaseURI = await serializer.serialize( - await lock.getAddress() - ) - await assert.equal( - serializedCustomBaseURI.tokenURISample, - `${baseTokenURI}${totalSupply}` - ) - }) - - describe('key ownership', () => { - let purchasers - const keyPrice = ethers.parseEther('0.01') - - // eslint-disable-next-line func-names - beforeEach(async function () { - const [, ..._purchasers] = await ethers.getSigners() - const maxNumberOfKeys = await lock.maxNumberOfKeys() - purchasers = _purchasers.slice(0, parseInt(maxNumberOfKeys.toString())) // prevent soldout revert - - // purchase keys - await lock.connect(purchasers[0]).purchase( - [], - await Promise.all(purchasers.map((p) => p.getAddress())), - purchasers.map(() => ADDRESS_ZERO), - purchasers.map(() => ADDRESS_ZERO), - purchasers.map(() => '0x'), - { value: keyPrice * BigInt(purchasers.length) } - ) - }) - - it('contains all key owners', async () => { - const serialized = await serializer.serialize(await lock.getAddress()) - assert.deepEqual( - serialized.keyOwners, - await Promise.all(purchasers.map((p) => p.getAddress())) - ) - }) - - it('containes key expirations', async () => { - const serialized = await serializer.serialize(await lock.getAddress()) - assert.equal(serialized.expirationTimestamps.length, purchasers.length) - }) - }) - }) -})