Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion src/IPatchworkProtocol.sol
Original file line number Diff line number Diff line change
Expand Up @@ -455,15 +455,17 @@ interface IPatchworkProtocol {

/**
@notice Create a new patch
@param owner The owner of the patch
@param originalNFTAddress Address of the original NFT
@param originalNFTTokenId Token ID of the original NFT
@param patchAddress Address of the IPatchworkPatch to mint
@return tokenId Token ID of the newly created patch
*/
function createPatch(address originalNFTAddress, uint originalNFTTokenId, address patchAddress) external returns (uint256 tokenId);
function createPatch(address owner, address originalNFTAddress, uint originalNFTTokenId, address patchAddress) external returns (uint256 tokenId);

/**
@notice Create a new account patch
@param owner The owner of the patch
@param originalAddress Address of the original account
@param patchAddress Address of the IPatchworkPatch to mint
@return tokenId Token ID of the newly created patch
Expand Down
1 change: 1 addition & 0 deletions src/PatchworkPatch.sol
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ abstract contract PatchworkPatch is PatchworkNFT, IPatchworkPatch {
@dev See {IERC721-ownerOf}
*/
function ownerOf(uint256 tokenId) public view virtual override(ERC721, IERC721) returns (address) {
// Default is inherited ownership
return IERC721(_patchedAddresses[tokenId]).ownerOf(_patchedTokenIds[tokenId]);
}

Expand Down
9 changes: 4 additions & 5 deletions src/PatchworkProtocol.sol
Original file line number Diff line number Diff line change
Expand Up @@ -146,16 +146,15 @@ contract PatchworkProtocol is IPatchworkProtocol {
/**
@dev See {IPatchworkProtocol-createAccountPatch}
*/
function createPatch(address originalNFTAddress, uint originalNFTTokenId, address patchAddress) public returns (uint256 tokenId) {
function createPatch(address owner, address originalNFTAddress, uint originalNFTTokenId, address patchAddress) public returns (uint256 tokenId) {
IPatchworkPatch patch = IPatchworkPatch(patchAddress);
string memory scopeName = patch.getScopeName();
// mint a Patch that is soulbound to the originalNFT using the contract address at patchAddress which must support Patchwork metadata
Scope storage scope = _mustHaveScope(scopeName);
_mustBeWhitelisted(scopeName, scope, patchAddress);
address tokenOwner = IERC721(originalNFTAddress).ownerOf(originalNFTTokenId);
if (scope.owner == msg.sender || scope.operators[msg.sender]) {
// continue
} else if (scope.allowUserPatch && msg.sender == tokenOwner) {
} else if (scope.allowUserPatch) {
// continue
} else {
revert NotAuthorized(msg.sender);
Expand All @@ -166,8 +165,8 @@ contract PatchworkProtocol is IPatchworkProtocol {
revert AlreadyPatched(originalNFTAddress, originalNFTTokenId, patchAddress);
}
scope.uniquePatches[_hash] = true;
tokenId = patch.mintPatch(tokenOwner, originalNFTAddress, originalNFTTokenId);
emit Patch(tokenOwner, originalNFTAddress, originalNFTTokenId, patchAddress, tokenId);
tokenId = patch.mintPatch(owner, originalNFTAddress, originalNFTTokenId);
emit Patch(owner, originalNFTAddress, originalNFTTokenId, patchAddress, tokenId);
return tokenId;
}

Expand Down
6 changes: 3 additions & 3 deletions test/PatchworkNFTReferences.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -68,12 +68,12 @@ contract PatchworkNFTCombinedTest is Test {
assertEq(_userAddress, _testFragmentLiteRefNFT.ownerOf(fragmentTokenId)); // TODO why doesn't this cover the branch != address(0)
vm.expectRevert(abi.encodeWithSelector(IPatchworkProtocol.NotAuthorized.selector, _user2Address));
vm.prank(_user2Address);
uint256 patchTokenId = _prot.createPatch(address(_testBaseNFT), baseTokenId, address(_testPatchLiteRefNFT));
uint256 patchTokenId = _prot.createPatch(_userAddress, address(_testBaseNFT), baseTokenId, address(_testPatchLiteRefNFT));
vm.expectRevert(abi.encodeWithSelector(IPatchworkProtocol.NotAuthorized.selector, _userAddress));
vm.prank(_userAddress); // must have user patch enabled
patchTokenId = _prot.createPatch(address(_testBaseNFT), baseTokenId, address(_testPatchLiteRefNFT));
patchTokenId = _prot.createPatch(_userAddress, address(_testBaseNFT), baseTokenId, address(_testPatchLiteRefNFT));
vm.prank(_scopeOwner);
patchTokenId = _prot.createPatch(address(_testBaseNFT), baseTokenId, address(_testPatchLiteRefNFT));
patchTokenId = _prot.createPatch(_userAddress, address(_testBaseNFT), baseTokenId, address(_testPatchLiteRefNFT));
vm.expectRevert(abi.encodeWithSelector(IPatchworkProtocol.NotAuthorized.selector, _userAddress));
vm.prank(_userAddress); // can't call directly
_testFragmentLiteRefNFT.assign(fragmentTokenId, address(_testPatchLiteRefNFT), patchTokenId);
Expand Down
17 changes: 12 additions & 5 deletions test/PatchworkPatch.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ contract PatchworkPatchTest is Test {
function testLocks() public {
uint256 baseTokenId = _testBaseNFT.mint(_userAddress);
vm.prank(_scopeOwner);
uint256 patchTokenId = _prot.createPatch(address(_testBaseNFT), baseTokenId, address(_testPatchLiteRefNFT));
uint256 patchTokenId = _prot.createPatch(_userAddress, address(_testBaseNFT), baseTokenId, address(_testPatchLiteRefNFT));
bool locked = _testPatchLiteRefNFT.locked(patchTokenId);
assertFalse(locked);
vm.expectRevert(abi.encodeWithSelector(IPatchworkProtocol.CannotLockSoulboundPatch.selector, _testPatchLiteRefNFT));
Expand All @@ -69,21 +69,28 @@ contract PatchworkPatchTest is Test {
function testBurn() public {
uint256 baseTokenId = _testBaseNFT.mint(_userAddress);
vm.prank(_scopeOwner);
uint256 patchTokenId = _prot.createPatch(address(_testBaseNFT), baseTokenId, address(_testPatchLiteRefNFT));
uint256 patchTokenId = _prot.createPatch(_userAddress, address(_testBaseNFT), baseTokenId, address(_testPatchLiteRefNFT));
vm.expectRevert(abi.encodeWithSelector(IPatchworkProtocol.UnsupportedOperation.selector));
_testPatchLiteRefNFT.burn(patchTokenId);
}

function testOtherOwnerDisallowed() public {
uint256 baseTokenId = _testBaseNFT.mint(_userAddress);
vm.prank(_scopeOwner);
vm.expectRevert(abi.encodeWithSelector(IPatchworkProtocol.NotAuthorized.selector, _user2Address));
_prot.createPatch(_user2Address, address(_testBaseNFT), baseTokenId, address(_testPatchLiteRefNFT));
}

function testPatchFragment() public {
vm.startPrank(_scopeOwner);
uint256 baseTokenId = _testBaseNFT.mint(_userAddress);
uint256 baseTokenId2 = _testBaseNFT.mint(_user2Address);
uint256 baseTokenId3 = _testBaseNFT.mint(_userAddress);
TestPatchFragmentNFT testPatchFragmentNFT = new TestPatchFragmentNFT(address(_prot));
_testPatchLiteRefNFT.registerReferenceAddress(address(testPatchFragmentNFT));
uint256 liteRefId = _prot.createPatch(address(_testBaseNFT), baseTokenId, address(_testPatchLiteRefNFT));
uint256 liteRefId2 = _prot.createPatch(address(_testBaseNFT), baseTokenId2, address(_testPatchLiteRefNFT));
uint256 fragmentTokenId = _prot.createPatch(address(_testBaseNFT), baseTokenId3, address(testPatchFragmentNFT));
uint256 liteRefId = _prot.createPatch(_userAddress, address(_testBaseNFT), baseTokenId, address(_testPatchLiteRefNFT));
uint256 liteRefId2 = _prot.createPatch(_user2Address, address(_testBaseNFT), baseTokenId2, address(_testPatchLiteRefNFT));
uint256 fragmentTokenId = _prot.createPatch(_userAddress, address(_testBaseNFT), baseTokenId3, address(testPatchFragmentNFT));
// cannot assign patch to a literef that this person does not own
vm.expectRevert(abi.encodeWithSelector(IPatchworkProtocol.NotAuthorized.selector, _scopeOwner));
_prot.assignNFT(address(testPatchFragmentNFT), fragmentTokenId, address(_testPatchLiteRefNFT), liteRefId2);
Expand Down
38 changes: 19 additions & 19 deletions test/PatchworkProtocol.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ contract PatchworkProtocolTest is Test {
_prot.claimScope(_scopeName);
_prot.setScopeRules(_scopeName, false, false, false);
uint256 _testBaseNFTTokenId = _testBaseNFT.mint(_userAddress);
uint256 tokenId = _prot.createPatch(address(_testBaseNFT), _testBaseNFTTokenId, address(_testPatchLiteRefNFT));
uint256 tokenId = _prot.createPatch(_userAddress, address(_testBaseNFT), _testBaseNFTTokenId, address(_testPatchLiteRefNFT));
assertEq(tokenId, 0);
}

Expand All @@ -136,7 +136,7 @@ contract PatchworkProtocolTest is Test {
_prot.claimScope(_scopeName);
uint256 _testBaseNFTTokenId = _testBaseNFT.mint(_userAddress);
vm.expectRevert(abi.encodeWithSelector(IPatchworkProtocol.NotWhitelisted.selector, _scopeName, address(_testPatchLiteRefNFT)));
_prot.createPatch(address(_testBaseNFT), _testBaseNFTTokenId, address(_testPatchLiteRefNFT));
_prot.createPatch(_userAddress, address(_testBaseNFT), _testBaseNFTTokenId, address(_testPatchLiteRefNFT));
}

function testCreatePatchNFTVerified() public {
Expand All @@ -149,7 +149,7 @@ contract PatchworkProtocolTest is Test {
vm.startPrank(_scopeOwner);
_prot.addWhitelist(_scopeName, address(_testPatchLiteRefNFT));
uint256 _testBaseNFTTokenId = _testBaseNFT.mint(_userAddress);
uint256 tokenId = _prot.createPatch(address(_testBaseNFT), _testBaseNFTTokenId, address(_testPatchLiteRefNFT));
uint256 tokenId = _prot.createPatch(_userAddress, address(_testBaseNFT), _testBaseNFTTokenId, address(_testPatchLiteRefNFT));
assertEq(tokenId, 0);
vm.stopPrank();
vm.expectRevert(abi.encodeWithSelector(IPatchworkProtocol.NotAuthorized.selector, _userAddress));
Expand All @@ -158,7 +158,7 @@ contract PatchworkProtocolTest is Test {
vm.startPrank(_scopeOwner);
_prot.removeWhitelist(_scopeName, address(_testPatchLiteRefNFT));
vm.expectRevert(abi.encodeWithSelector(IPatchworkProtocol.NotWhitelisted.selector, _scopeName, address(_testPatchLiteRefNFT)));
tokenId = _prot.createPatch(address(_testBaseNFT), _testBaseNFTTokenId + 1, address(_testPatchLiteRefNFT));
tokenId = _prot.createPatch(_userAddress, address(_testBaseNFT), _testBaseNFTTokenId + 1, address(_testPatchLiteRefNFT));
}

function testUserPermissions() public {
Expand All @@ -174,12 +174,12 @@ contract PatchworkProtocolTest is Test {
vm.stopPrank();
vm.startPrank(_userAddress);
vm.expectRevert(abi.encodeWithSelector(IPatchworkProtocol.NotAuthorized.selector, _userAddress));
uint256 patchTokenId = _prot.createPatch(address(_testBaseNFT), _testBaseNFTTokenId, address(_testPatchLiteRefNFT));
uint256 patchTokenId = _prot.createPatch(_userAddress, address(_testBaseNFT), _testBaseNFTTokenId, address(_testPatchLiteRefNFT));
vm.stopPrank();
vm.prank(_scopeOwner);
_prot.setScopeRules(_scopeName, true, false, true);
vm.startPrank(_userAddress);
patchTokenId = _prot.createPatch(address(_testBaseNFT), _testBaseNFTTokenId, address(_testPatchLiteRefNFT));
patchTokenId = _prot.createPatch(_userAddress, address(_testBaseNFT), _testBaseNFTTokenId, address(_testPatchLiteRefNFT));
vm.expectRevert(abi.encodeWithSelector(IPatchworkProtocol.NotAuthorized.selector, _userAddress));
_prot.assignNFT(address(_testFragmentLiteRefNFT), fragmentTokenId, address(_testPatchLiteRefNFT), patchTokenId);
vm.stopPrank();
Expand All @@ -199,14 +199,14 @@ contract PatchworkProtocolTest is Test {

uint256 _testBaseNFTTokenId = _testBaseNFT.mint(_userAddress);
vm.expectRevert(abi.encodeWithSelector(IPatchworkProtocol.ScopeDoesNotExist.selector, _scopeName));
_prot.createPatch(address(_testBaseNFT), _testBaseNFTTokenId, address(_testPatchLiteRefNFT));
_prot.createPatch(_userAddress, address(_testBaseNFT), _testBaseNFTTokenId, address(_testPatchLiteRefNFT));

vm.startPrank(_scopeOwner);
_prot.claimScope(_scopeName);
_prot.setScopeRules(_scopeName, false, false, false);
uint256 patchTokenId = _prot.createPatch(address(_testBaseNFT), _testBaseNFTTokenId, address(_testPatchLiteRefNFT));
uint256 patchTokenId = _prot.createPatch(_userAddress, address(_testBaseNFT), _testBaseNFTTokenId, address(_testPatchLiteRefNFT));
vm.expectRevert(abi.encodeWithSelector(IPatchworkProtocol.AlreadyPatched.selector, address(_testBaseNFT), _testBaseNFTTokenId, address(_testPatchLiteRefNFT)));
patchTokenId = _prot.createPatch(address(_testBaseNFT), _testBaseNFTTokenId, address(_testPatchLiteRefNFT));
patchTokenId = _prot.createPatch(_userAddress, address(_testBaseNFT), _testBaseNFTTokenId, address(_testPatchLiteRefNFT));

uint256 fragmentTokenId = _testFragmentLiteRefNFT.mint(_userAddress);
assertEq(_testFragmentLiteRefNFT.ownerOf(fragmentTokenId), _userAddress);
Expand Down Expand Up @@ -305,7 +305,7 @@ contract PatchworkProtocolTest is Test {
_testPatchLiteRefNFT.registerReferenceAddress(address(_testFragmentLiteRefNFT));
vm.stopPrank();
vm.startPrank(_userAddress);
uint256 patchTokenId = _prot.createPatch(address(_testBaseNFT), _testBaseNFTTokenId, address(_testPatchLiteRefNFT));
uint256 patchTokenId = _prot.createPatch(_userAddress, address(_testBaseNFT), _testBaseNFTTokenId, address(_testPatchLiteRefNFT));

uint256 fragmentTokenId = _testFragmentLiteRefNFT.mint(_userAddress);
uint256 user2FragmentTokenId = _testFragmentLiteRefNFT.mint(_user2Address);
Expand Down Expand Up @@ -355,7 +355,7 @@ contract PatchworkProtocolTest is Test {
vm.startPrank(_scopeOwner);
_prot.claimScope(_scopeName);
_prot.setScopeRules(_scopeName, false, false, false);
uint256 patchTokenId = _prot.createPatch(address(_testBaseNFT), _testBaseNFTTokenId, address(_testPatchLiteRefNFT));
uint256 patchTokenId = _prot.createPatch(_userAddress, address(_testBaseNFT), _testBaseNFTTokenId, address(_testPatchLiteRefNFT));

uint256 fragmentTokenId = _testFragmentLiteRefNFT.mint(_user2Address);
assertEq(_testFragmentLiteRefNFT.ownerOf(fragmentTokenId), _user2Address);
Expand All @@ -376,7 +376,7 @@ contract PatchworkProtocolTest is Test {
vm.startPrank(_scopeOwner);
_prot.claimScope(_scopeName);
_prot.setScopeRules(_scopeName, false, false, false);
uint256 patchTokenId = _prot.createPatch(address(_testBaseNFT), _testBaseNFTTokenId, address(_testPatchLiteRefNFT));
uint256 patchTokenId = _prot.createPatch(_userAddress, address(_testBaseNFT), _testBaseNFTTokenId, address(_testPatchLiteRefNFT));

_testPatchLiteRefNFT.registerReferenceAddress(address(_testFragmentLiteRefNFT));
//Register _testFragmentLiteRefNFT to _testFragmentLiteRefNFT to allow recursion
Expand Down Expand Up @@ -447,7 +447,7 @@ contract PatchworkProtocolTest is Test {
vm.startPrank(_scopeOwner);
_prot.claimScope(_scopeName);
_prot.setScopeRules(_scopeName, false, false, false);
uint256 patchTokenId = _prot.createPatch(address(_testBaseNFT), _testBaseNFTTokenId, address(_testPatchLiteRefNFT));
uint256 patchTokenId = _prot.createPatch(_userAddress, address(_testBaseNFT), _testBaseNFTTokenId, address(_testPatchLiteRefNFT));

_testPatchLiteRefNFT.registerReferenceAddress(address(_testMultiFragmentNFT));
_prot.assignNFT(address(_testMultiFragmentNFT), fragment1, address(_testPatchLiteRefNFT), patchTokenId);
Expand All @@ -462,7 +462,7 @@ contract PatchworkProtocolTest is Test {
vm.startPrank(_scopeOwner);
_prot.claimScope(_scopeName);
_prot.setScopeRules(_scopeName, false, false, false);
uint256 patchTokenId = _prot.createPatch(address(_testBaseNFT), _testBaseNFTTokenId, address(_testPatchLiteRefNFT));
uint256 patchTokenId = _prot.createPatch(_userAddress, address(_testBaseNFT), _testBaseNFTTokenId, address(_testPatchLiteRefNFT));
address[] memory fragmentAddresses = new address[](8);
uint256[] memory fragments = new uint256[](8);

Expand Down Expand Up @@ -582,7 +582,7 @@ contract PatchworkProtocolTest is Test {
vm.stopPrank();

vm.startPrank(_userAddress);
uint256 patchTokenId = _prot.createPatch(address(_testBaseNFT), _testBaseNFTTokenId, address(_testPatchLiteRefNFT));
uint256 patchTokenId = _prot.createPatch(_userAddress, address(_testBaseNFT), _testBaseNFTTokenId, address(_testPatchLiteRefNFT));
address[] memory fragmentAddresses = new address[](8);
uint256[] memory fragments = new uint256[](8);
uint256[] memory user2Fragments = new uint256[](8);
Expand Down Expand Up @@ -689,7 +689,7 @@ contract PatchworkProtocolTest is Test {
// test with patch
uint256 _testBaseNFTTokenId = _testBaseNFT.mint(_userAddress);
vm.prank(_scopeOwner);
uint256 patchTokenId = _prot.createPatch(address(_testBaseNFT), _testBaseNFTTokenId, address(_testPatchLiteRefNFT));
uint256 patchTokenId = _prot.createPatch(_userAddress, address(_testBaseNFT), _testBaseNFTTokenId, address(_testPatchLiteRefNFT));
vm.prank(_userAddress);
_testBaseNFT.transferFrom(_userAddress, _user2Address, _testBaseNFTTokenId);
assertEq(_user2Address, _testPatchLiteRefNFT.ownerOf(patchTokenId));
Expand All @@ -706,7 +706,7 @@ contract PatchworkProtocolTest is Test {
vm.startPrank(_scopeOwner);
_prot.claimScope(_scopeName);
_prot.setScopeRules(_scopeName, false, false, false);
uint256 patchTokenId = _prot.createPatch(address(_testBaseNFT), _testBaseNFTTokenId, address(_testPatchLiteRefNFT));
uint256 patchTokenId = _prot.createPatch(_userAddress, address(_testBaseNFT), _testBaseNFTTokenId, address(_testPatchLiteRefNFT));

_testPatchLiteRefNFT.registerReferenceAddress(address(_testFragmentLiteRefNFT));
//Register _testFragmentLiteRefNFT to _testFragmentLiteRefNFT to allow recursion
Expand Down Expand Up @@ -771,7 +771,7 @@ contract PatchworkProtocolTest is Test {
vm.startPrank(_scopeOwner);
_prot.claimScope(_scopeName);
_prot.setScopeRules(_scopeName, false, false, false);
uint256 patchTokenId = _prot.createPatch(address(_testBaseNFT), _testBaseNFTTokenId, address(_testPatchLiteRefNFT));
uint256 patchTokenId = _prot.createPatch(_userAddress, address(_testBaseNFT), _testBaseNFTTokenId, address(_testPatchLiteRefNFT));

_testPatchLiteRefNFT.registerReferenceAddress(address(_testFragmentLiteRefNFT));
//Register _testFragmentLiteRefNFT to _testFragmentLiteRefNFT to allow recursion
Expand Down Expand Up @@ -888,7 +888,7 @@ contract PatchworkProtocolTest is Test {
uint256 frag1 = _testFragmentLiteRefNFT.mint(_userAddress);
uint256 frag2 = testFrag2.mint(_userAddress);
uint256 _testBaseNFTTokenId = _testBaseNFT.mint(_userAddress);
uint256 patchTokenId = _prot.createPatch(address(_testBaseNFT), _testBaseNFTTokenId, address(_testPatchLiteRefNFT));
uint256 patchTokenId = _prot.createPatch(_userAddress, address(_testBaseNFT), _testBaseNFTTokenId, address(_testPatchLiteRefNFT));
_prot.assignNFT(address(_testFragmentLiteRefNFT), frag1, address(_testPatchLiteRefNFT), patchTokenId);
// The second assign succeeding combined with the assertion that they are equal ref values means there is no collision in the scope.
_prot.assignNFT(address(testFrag2), frag2, address(_testFragmentLiteRefNFT), frag1);
Expand Down
8 changes: 6 additions & 2 deletions test/nfts/TestPatchLiteRefNFT.sol
Original file line number Diff line number Diff line change
Expand Up @@ -170,15 +170,19 @@ contract TestPatchLiteRefNFT is PatchworkPatch, PatchworkLiteRef {
// TODO bulk insert for fewer stores
}

function mintPatch(address originalNFTOwner, address originalNFTAddress, uint originalNFTTokenId) external returns (uint256 tokenId){
function mintPatch(address owner, address originalNFTAddress, uint originalNFTTokenId) external returns (uint256 tokenId){
if (msg.sender != _manager) {
revert();
}
// require inherited ownership
if (IERC721(originalNFTAddress).ownerOf(originalNFTTokenId) != owner) {
revert IPatchworkProtocol.NotAuthorized(owner);
}
// Just for testing
tokenId = _nextTokenId;
_nextTokenId++;
_storePatch(tokenId, originalNFTAddress, originalNFTTokenId);
_safeMint(originalNFTOwner, tokenId);
_safeMint(owner, tokenId);
_metadataStorage[tokenId] = new uint256[](3);
return tokenId;
}
Expand Down