From 17724937f7132f311d54271749a47ee885768ead Mon Sep 17 00:00:00 2001 From: bpolania Date: Tue, 10 Jun 2025 23:42:31 -0700 Subject: [PATCH 1/3] Mint and integration tests --- .../resources/IPAsset.py | 40 ++++ .../integration/test_integration_ip_asset.py | 212 ++++++++++++++++++ 2 files changed, 252 insertions(+) diff --git a/src/story_protocol_python_sdk/resources/IPAsset.py b/src/story_protocol_python_sdk/resources/IPAsset.py index a86e7c9..af24cd8 100644 --- a/src/story_protocol_python_sdk/resources/IPAsset.py +++ b/src/story_protocol_python_sdk/resources/IPAsset.py @@ -18,6 +18,8 @@ from story_protocol_python_sdk.utils.sign import Sign from story_protocol_python_sdk.utils.constants import ZERO_ADDRESS, ZERO_HASH +from story_protocol_python_sdk.abi.SPGNFTImpl.SPGNFTImpl_client import SPGNFTImplClient + class IPAsset: """ IPAssetClient allows you to create, get, and list IP Assets with Story @@ -42,10 +44,48 @@ def __init__(self, web3: Web3, account, chain_id: int): self.core_metadata_module_client = CoreMetadataModuleClient(web3) self.access_controller_client = AccessControllerClient(web3) self.pi_license_template_client = PILicenseTemplateClient(web3) + self.nft_client = SPGNFTImplClient(web3) self.license_terms_util = LicenseTerms(web3) self.sign_util = Sign(web3, self.chain_id, self.account) + def mint( + self, + nft_contract: str, + to_address: str, + metadata_uri: str, + metadata_hash: bytes, + allow_duplicates: bool = False, + tx_options: dict = None + ): + spg_nft_client = SPGNFTImplClient(self.web3, contract_address=nft_contract) + + def build_mint_transaction(to, metadata_uri, metadata_hash, allow_duplicates, transaction_options): + return spg_nft_client.contract.functions.mint( + to, + metadata_uri, + metadata_hash, + allow_duplicates + ).build_transaction(transaction_options) + + response = build_and_send_transaction( + self.web3, + self.account, + build_mint_transaction, + to_address, + metadata_uri, + metadata_hash, + allow_duplicates, + tx_options=tx_options + ) + + tx_hash = response['tx_hash'] + # Ensure the transaction hash starts with "0x" + if isinstance(tx_hash, str) and not tx_hash.startswith('0x'): + tx_hash = '0x' + tx_hash + + return tx_hash + def register( self, nft_contract: str, diff --git a/tests/integration/test_integration_ip_asset.py b/tests/integration/test_integration_ip_asset.py index 01b791b..9fff5b0 100644 --- a/tests/integration/test_integration_ip_asset.py +++ b/tests/integration/test_integration_ip_asset.py @@ -400,3 +400,215 @@ def test_register_ip_and_attach_pil_terms(self, story_client, nft_collection, pa assert isinstance(result['tx_hash'], str) and result['tx_hash'] assert isinstance(result['ip_id'], str) and result['ip_id'] assert isinstance(result['license_terms_ids'], list) and result['license_terms_ids'] + +# BORIS + +# Add this test class to your existing test_integration_ip_asset.py file + +class TestIPAssetMint: + @pytest.fixture(scope="module") + def nft_collection(self, story_client): + tx_data = story_client.NFTClient.create_nft_collection( + name="test-mint-collection", + symbol="MINT", + max_supply=100, + is_public_minting=True, + mint_open=True, + contract_uri="test-mint-uri", + mint_fee_recipient=account.address, + ) + return tx_data['nft_contract'] + + def test_mint_basic(self, story_client, nft_collection): + """Test basic minting functionality""" + metadata_uri = "https://example.com/metadata/1.json" + metadata_hash = web3.keccak(text="test-metadata-content") + + response = story_client.IPAsset.mint( + nft_contract=nft_collection, + to_address=account.address, + metadata_uri=metadata_uri, + metadata_hash=metadata_hash, + allow_duplicates=False + ) + + assert response is not None + assert isinstance(response, str) + assert len(response) > 0 + assert response.startswith("0x") + + # Wait for transaction confirmation to verify it was successful + receipt = story_client.web3.eth.wait_for_transaction_receipt(response) + assert receipt.status == 1 + + def test_mint_with_duplicates_allowed(self, story_client, nft_collection): + """Test minting with duplicate metadata allowed""" + metadata_uri = "https://example.com/metadata/duplicate.json" + metadata_hash = web3.keccak(text="duplicate-metadata-content") + + # First mint + response1 = story_client.IPAsset.mint( + nft_contract=nft_collection, + to_address=account.address, + metadata_uri=metadata_uri, + metadata_hash=metadata_hash, + allow_duplicates=True + ) + + assert response1 is not None + receipt1 = story_client.web3.eth.wait_for_transaction_receipt(response1) + assert receipt1.status == 1 + + # Second mint with same metadata (should succeed with allow_duplicates=True) + response2 = story_client.IPAsset.mint( + nft_contract=nft_collection, + to_address=account.address, + metadata_uri=metadata_uri, + metadata_hash=metadata_hash, + allow_duplicates=True + ) + + assert response2 is not None + receipt2 = story_client.web3.eth.wait_for_transaction_receipt(response2) + assert receipt2.status == 1 + + # Verify different transaction hashes + assert response1 != response2 + + def test_mint_to_different_address(self, story_client, nft_collection): + """Test minting to a different recipient address""" + # Create a different recipient address for testing + different_account = story_client.web3.eth.account.create() + recipient_address = different_account.address + + metadata_uri = "https://example.com/metadata/different-recipient.json" + metadata_hash = web3.keccak(text="different-recipient-metadata") + + response = story_client.IPAsset.mint( + nft_contract=nft_collection, + to_address=recipient_address, + metadata_uri=metadata_uri, + metadata_hash=metadata_hash, + allow_duplicates=False + ) + + assert response is not None + receipt = story_client.web3.eth.wait_for_transaction_receipt(response) + assert receipt.status == 1 + + def test_mint_with_various_metadata_formats(self, story_client, nft_collection): + """Test minting with different metadata URI formats""" + test_cases = [ + { + "uri": "ipfs://QmYwAPJzv5CZsnA625s3Xf2nemtYgPpHdWEz79ojWnPbdG", + "content": "ipfs-metadata" + }, + { + "uri": "https://gateway.pinata.cloud/ipfs/QmTest123", + "content": "pinata-gateway-metadata" + }, + { + "uri": "ar://abc123def456", + "content": "arweave-metadata" + }, + { + "uri": "", # Empty URI + "content": "empty-uri-metadata" + } + ] + + for i, test_case in enumerate(test_cases): + metadata_hash = web3.keccak(text=f"{test_case['content']}-{i}") + + response = story_client.IPAsset.mint( + nft_contract=nft_collection, + to_address=account.address, + metadata_uri=test_case["uri"], + metadata_hash=metadata_hash, + allow_duplicates=False + ) + + assert response is not None + receipt = story_client.web3.eth.wait_for_transaction_receipt(response) + assert receipt.status == 1 + + def test_mint_with_zero_hash(self, story_client, nft_collection): + """Test minting with zero hash""" + metadata_uri = "https://example.com/metadata/zero-hash.json" + zero_hash = b'\x00' * 32 # 32 bytes of zeros + + response = story_client.IPAsset.mint( + nft_contract=nft_collection, + to_address=account.address, + metadata_uri=metadata_uri, + metadata_hash=zero_hash, + allow_duplicates=False + ) + + assert response is not None + receipt = story_client.web3.eth.wait_for_transaction_receipt(response) + assert receipt.status == 1 + + + def test_mint_error_cases(self, story_client, nft_collection): + """Test various error cases for minting""" + metadata_uri = "https://example.com/metadata/error-test.json" + metadata_hash = web3.keccak(text="error-test-metadata") + + # Test with invalid address format (not a valid hex address) + with pytest.raises(Exception): + story_client.IPAsset.mint( + nft_contract=nft_collection, + to_address="invalid-address-format", + metadata_uri=metadata_uri, + metadata_hash=metadata_hash, + allow_duplicates=False + ) + + # Test with invalid metadata hash format (wrong length - too short) + with pytest.raises(Exception): + story_client.IPAsset.mint( + nft_contract=nft_collection, + to_address=account.address, + metadata_uri=metadata_uri, + metadata_hash=b"too_short", # bytes32 should be 32 bytes + allow_duplicates=False + ) + + # Test with invalid metadata hash format (wrong length - too long) + with pytest.raises(Exception): + story_client.IPAsset.mint( + nft_contract=nft_collection, + to_address=account.address, + metadata_uri=metadata_uri, + metadata_hash=b"this_is_way_too_long_for_bytes32_format_and_should_fail", + allow_duplicates=False + ) + + def test_mint_with_existing_metadata_hash_no_duplicates(self, story_client, nft_collection): + """Test that minting with existing metadata hash fails when duplicates not allowed""" + metadata_uri = "https://example.com/metadata/no-duplicates.json" + metadata_hash = web3.keccak(text="no-duplicates-metadata") + + # First mint should succeed + response1 = story_client.IPAsset.mint( + nft_contract=nft_collection, + to_address=account.address, + metadata_uri=metadata_uri, + metadata_hash=metadata_hash, + allow_duplicates=False + ) + + assert response1 is not None + receipt1 = story_client.web3.eth.wait_for_transaction_receipt(response1) + assert receipt1.status == 1 + + # Second mint with same metadata hash should fail when allow_duplicates=False + with pytest.raises(Exception): + story_client.IPAsset.mint( + nft_contract=nft_collection, + to_address=account.address, + metadata_uri=metadata_uri, + metadata_hash=metadata_hash, + allow_duplicates=False + ) From 3f9605ddcf757eaefe82e58d0c0c47c2571a1f2f Mon Sep 17 00:00:00 2001 From: bpolania Date: Thu, 12 Jun 2025 23:42:59 -0700 Subject: [PATCH 2/3] Update test_integration_ip_asset.py --- tests/integration/test_integration_ip_asset.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/tests/integration/test_integration_ip_asset.py b/tests/integration/test_integration_ip_asset.py index 9fff5b0..3b3143b 100644 --- a/tests/integration/test_integration_ip_asset.py +++ b/tests/integration/test_integration_ip_asset.py @@ -400,9 +400,7 @@ def test_register_ip_and_attach_pil_terms(self, story_client, nft_collection, pa assert isinstance(result['tx_hash'], str) and result['tx_hash'] assert isinstance(result['ip_id'], str) and result['ip_id'] assert isinstance(result['license_terms_ids'], list) and result['license_terms_ids'] - -# BORIS - + # Add this test class to your existing test_integration_ip_asset.py file class TestIPAssetMint: From a78ed37e4d11cdb080bf8d4c0c4ec08332840ab6 Mon Sep 17 00:00:00 2001 From: bpolania Date: Thu, 12 Jun 2025 23:48:53 -0700 Subject: [PATCH 3/3] Update IPAsset.py --- src/story_protocol_python_sdk/resources/IPAsset.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/story_protocol_python_sdk/resources/IPAsset.py b/src/story_protocol_python_sdk/resources/IPAsset.py index af24cd8..d095f90 100644 --- a/src/story_protocol_python_sdk/resources/IPAsset.py +++ b/src/story_protocol_python_sdk/resources/IPAsset.py @@ -44,7 +44,6 @@ def __init__(self, web3: Web3, account, chain_id: int): self.core_metadata_module_client = CoreMetadataModuleClient(web3) self.access_controller_client = AccessControllerClient(web3) self.pi_license_template_client = PILicenseTemplateClient(web3) - self.nft_client = SPGNFTImplClient(web3) self.license_terms_util = LicenseTerms(web3) self.sign_util = Sign(web3, self.chain_id, self.account)