Skip to content

Commit

Permalink
Migrate slug to data storage lukso-network/LIPs#136
Browse files Browse the repository at this point in the history
  • Loading branch information
lykhonis committed Nov 3, 2022
1 parent 05fa24a commit 54ef43e
Show file tree
Hide file tree
Showing 3 changed files with 42 additions and 11 deletions.
2 changes: 0 additions & 2 deletions contracts/page/IUniversalPageName.sol
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@ interface IUniversalPageName is ILSP8IdentifiableDigitalAsset {
event ReservedName(address indexed recipient, bytes32 indexed tokenId);
event ReleasedName(address indexed owner, bytes32 indexed tokenId);

function tokenSlugOf(bytes32 tokenId) external view returns (string memory);

function reserve(address recipient, string memory slug) external payable;

function release(bytes32 tokenId) external;
Expand Down
26 changes: 20 additions & 6 deletions contracts/page/UniversalPageName.sol
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,15 @@ contract UniversalPageName is
{
bytes32 private constant _LSP8_TOKEN_ID_TYPE_KEY =
0x715f248956de7ce65e94d9d836bfead479f7e70d69b718d47bfe7b00e05b4fe4;
bytes32 private constant _LSP8_TOKEN_ID_HASH_FUNCTION_KEY =
0x200d7fc10771371795cceb6ad6d33d5959c47250486fb93f77f0c20088b90a97;
bytes12 private constant _LSP8_TOKEN_ID_VALUE_KEY_PREFIX = 0x60fcfd25909da10abc320000;

bytes private constant _LSP8_TOKEN_ID_TYPE_HASHED =
hex"00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000003";
bytes private constant _LSP8_TOKEN_ID_HASH_FUNCTION = "keccak256(bytes)";

// TODO: remove this!
mapping(bytes32 => string) private _tokenSlug;

modifier onlyOperator(bytes32 tokenId) {
Expand All @@ -34,20 +40,17 @@ contract UniversalPageName is
) external initializer {
LSP8IdentifiableDigitalAssetInitAbstract._initialize(name, symbol, owner);
_setData(_LSP8_TOKEN_ID_TYPE_KEY, _LSP8_TOKEN_ID_TYPE_HASHED);
_setData(_LSP8_TOKEN_ID_HASH_FUNCTION_KEY, _LSP8_TOKEN_ID_HASH_FUNCTION);
_setAdmin(admin);
_setBeneficiary(beneficiary);
}

function tokenSlugOf(bytes32 tokenId) external view override returns (string memory) {
return _tokenSlug[tokenId];
}

function reserve(address recipient, string memory slug) external payable override {
require(_isAllowedSlug(slug), "Invalid slug");
bytes32 tokenId = keccak256(bytes(slug));
IUniversalPageNameDelegate(admin()).permitReserve(msg.sender, msg.value, recipient, slug);
_mint(recipient, tokenId, false, "0x");
_tokenSlug[tokenId] = slug;
_setData(_tokenIdKey(_LSP8_TOKEN_ID_VALUE_KEY_PREFIX, tokenId), bytes(slug));
emit ReservedName(recipient, tokenId);
}

Expand All @@ -66,8 +69,19 @@ contract UniversalPageName is
super._beforeTokenTransfer(from, to, tokenId);
IUniversalPageNameDelegate(admin()).permitTransfer(msg.sender, from, to, tokenId);
if (to == address(0)) {
delete _tokenSlug[tokenId];
bytes32 tokenIdKey = _tokenIdKey(_LSP8_TOKEN_ID_VALUE_KEY_PREFIX, tokenId);
delete store[tokenIdKey];
}
}

function _tokenIdKey(bytes12 prefix, bytes32 tokenId) private pure returns (bytes32) {
bytes20 tokenIdPart = bytes20(tokenId);
bytes memory result = new bytes(32);
assembly {
mstore(add(result, 32), prefix)
mstore(add(result, 44), tokenIdPart)
}
return bytes32(result);
}

function _isAllowedSlug(string memory value) private pure returns (bool) {
Expand Down
25 changes: 22 additions & 3 deletions tests/page/UniversalPageNameTest.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,16 @@ describe('UniversalPageName', () => {
),
)

const tokenSlugOf = async (tokenId) => {
const data = await pageContract['getData(bytes32)'](
ethers.utils.hexConcat([
ethers.utils.hexDataSlice(ethers.utils.keccak256(ethers.utils.toUtf8Bytes('LSP8TokenIdValue')), 0, 10),
'0x0000',
ethers.utils.hexDataSlice(tokenId, 0, 20),
]))
return ethers.utils.toUtf8String(data)
}

beforeEach(async () => {
[deployer, owner, beneficiary, alice, bob] = await ethers.getSigners();
[aliceProfile, aliceOwner] = await deployProfile(deployer, alice)
Expand Down Expand Up @@ -105,6 +115,15 @@ describe('UniversalPageName', () => {
})
})

it('should setup token', async () => {
const [type, hashFunction] = await pageContract['getData(bytes32[])']([
ethers.utils.keccak256(ethers.utils.toUtf8Bytes('LSP8TokenIdType')),
ethers.utils.keccak256(ethers.utils.toUtf8Bytes('LSP8TokenIdHashFunction')),
])
expect(ethers.BigNumber.from(type).toNumber()).to.be.eq(3)
expect(ethers.utils.toUtf8String(hashFunction)).to.be.eq('keccak256(bytes)')
})

it('should not set owner', async () => {
await expect(contract.connect(bob).transferOwnership(bob.address)).to.be.revertedWith('not the owner')
})
Expand Down Expand Up @@ -190,7 +209,7 @@ describe('UniversalPageName', () => {
tokenId,
)
expect(await pageContract.totalSupply()).to.be.eq(ethers.BigNumber.from(1))
expect(await pageContract.tokenSlugOf(tokenId)).to.be.eq('test')
expect(await tokenSlugOf(tokenId)).to.be.eq('test')
expect(await pageContract.tokenOwnerOf(tokenId)).to.be.eq(aliceProfile.address)
})

Expand Down Expand Up @@ -300,7 +319,7 @@ describe('UniversalPageName', () => {
it('should release', async () => {
expect(await pageContract.totalSupply()).to.be.eq(1)
expect(await pageContract.tokenOwnerOf(tokenOne)).to.be.eq(aliceProfile.address)
expect(await pageContract.tokenSlugOf(tokenOne)).to.be.eq('token1')
expect(await tokenSlugOf(tokenOne)).to.be.eq('token1')
await expect(aliceOwner.connect(alice).execute(
aliceProfile.interface.encodeFunctionData('execute(uint256,address,uint256,bytes)',
[
Expand All @@ -318,7 +337,7 @@ describe('UniversalPageName', () => {
)).to.emit(pageContract, 'ReleasedName').withArgs(aliceProfile.address, tokenOne)
expect(await pageContract.totalSupply()).to.be.eq(ethers.BigNumber.from(0))
await expect(pageContract.tokenOwnerOf(tokenOne)).to.be.revertedWith('NonExistentTokenId')
expect(await pageContract.tokenSlugOf(tokenOne)).to.be.empty
expect(await tokenSlugOf(tokenOne)).to.be.empty
})

it('should reserve free after release', async () => {
Expand Down

0 comments on commit 54ef43e

Please sign in to comment.