diff --git a/package.json b/package.json index 5466639..b754244 100644 --- a/package.json +++ b/package.json @@ -74,7 +74,7 @@ "dependencies": { "@0xged/hardhat-deploy": "0.11.5", "@mean-finance/deterministic-factory": "1.10.0", - "@openzeppelin/contracts": "4.7.3", + "@openzeppelin/contracts": "4.8.0", "base64-sol": "1.0.1" }, "devDependencies": { diff --git a/solidity/contracts/DCAHubPositionDescriptorFixed.sol b/solidity/contracts/DCAHubPositionDescriptorFixed.sol new file mode 100644 index 0000000..509773d --- /dev/null +++ b/solidity/contracts/DCAHubPositionDescriptorFixed.sol @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity >=0.8.16; +import '@openzeppelin/contracts/access/Ownable2Step.sol'; +import '../interfaces/IDCAHubPositionDescriptor.sol'; +import '../libraries/DescriptorUtils.sol'; + +/// @title Describes NFT token positions +/// @notice Produces a string containing the URL for the NFT +contract DCAHubPositionDescriptorFixed is IDCAHubPositionDescriptor, Ownable2Step { + using Strings for uint256; + + string public baseURL; + + constructor(string memory _baseURL, address _firstOwner) { + baseURL = _baseURL; + super._transferOwnership(_firstOwner); + } + + /// @inheritdoc IDCAHubPositionDescriptor + function tokenURI(address _hub, uint256 _tokenId) external view returns (string memory) { + return string(abi.encodePacked(baseURL, block.chainid.toString(), '-', DescriptorUtils.addressToString(_hub), '-', _tokenId.toString())); + } + + function setBaseURL(string memory _newBaseURL) external onlyOwner { + baseURL = _newBaseURL; + } +} diff --git a/test/unit/dca-hub-position-descriptor-fixed.spec.ts b/test/unit/dca-hub-position-descriptor-fixed.spec.ts new file mode 100644 index 0000000..cec1238 --- /dev/null +++ b/test/unit/dca-hub-position-descriptor-fixed.spec.ts @@ -0,0 +1,47 @@ +import { ethers } from 'hardhat'; +import { DCAHubPositionDescriptorFixed, DCAHubPositionDescriptorFixed__factory } from '@typechained'; +import { expect } from 'chai'; +import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers'; + +describe('DCAHubPositionDescriptorFixed', () => { + let hub: SignerWithAddress; + let tokenId: number; + let DCAHubPositionDescriptorFixed: DCAHubPositionDescriptorFixed; + let chainId: number; + + before('Setup accounts and contracts', async () => { + tokenId = Math.floor(Math.random() * (10000 - 1)); + const factory = (await ethers.getContractFactory('DCAHubPositionDescriptorFixed')) as DCAHubPositionDescriptorFixed__factory; + const [signer, _hub] = await ethers.getSigners(); + hub = _hub; + DCAHubPositionDescriptorFixed = await factory.deploy('baseurl/', signer.address); + chainId = (await ethers.provider.getNetwork()).chainId; + }); + + describe('tokenURI', () => { + it('returns the correct url', async () => { + const baseURL = await DCAHubPositionDescriptorFixed.baseURL(); + const url = await getPositionURI(); + const expectedUrl = baseURL + chainId + '-' + hub.address + '-' + tokenId; + expect(url.toLowerCase()).to.be.equal(expectedUrl.toLowerCase()); + }); + }); + describe('setBaseURL', () => { + it('prevents to modify base url', async () => { + const newBaseURL = 'newurl/'; + const impersonate = await ethers.getSigner('0x8894E0a0c962CB723c1976a4421c95949bE2D4E3'); + await expect(DCAHubPositionDescriptorFixed.connect(impersonate.address).setBaseURL(newBaseURL)).to.be.reverted; + }); + + it('modifies base url', async () => { + const newBaseURL = 'newurl/'; + await DCAHubPositionDescriptorFixed.setBaseURL(newBaseURL); + expect(await DCAHubPositionDescriptorFixed.baseURL()).to.be.equal(newBaseURL); + }); + }); + + async function getPositionURI() { + const result = await DCAHubPositionDescriptorFixed.tokenURI(hub.address, tokenId); + return result; + } +}); diff --git a/yarn.lock b/yarn.lock index f9f580a..a5ecff8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1718,10 +1718,10 @@ resolved "https://registry.yarnpkg.com/@openzeppelin/contracts/-/contracts-4.6.0.tgz#c91cf64bc27f573836dba4122758b4743418c1b3" integrity sha512-8vi4d50NNya/bQqCmaVzvHNmwHvS0OBKb7HNtuNwEE3scXWrP31fKQoGxNMT+KbzmrNZzatE3QK5p2gFONI/hg== -"@openzeppelin/contracts@4.7.3": - version "4.7.3" - resolved "https://registry.yarnpkg.com/@openzeppelin/contracts/-/contracts-4.7.3.tgz#939534757a81f8d69cc854c7692805684ff3111e" - integrity sha512-dGRS0agJzu8ybo44pCIf3xBaPQN/65AIXNgK8+4gzKd5kbvlqyxryUYVLJv7fK98Seyd2hDZzVEHSWAh0Bt1Yw== +"@openzeppelin/contracts@4.8.0": + version "4.8.0" + resolved "https://registry.yarnpkg.com/@openzeppelin/contracts/-/contracts-4.8.0.tgz#6854c37df205dd2c056bdfa1b853f5d732109109" + integrity sha512-AGuwhRRL+NaKx73WKRNzeCxOCOCxpaqF+kp8TJ89QzAipSwZy/NoflkWaL9bywXFRhIzXt8j38sfF7KBKCPWLw== "@openzeppelin/test-helpers@^0.5.15": version "0.5.16"