Skip to content

Commit

Permalink
Fixed transaction imbalance when burning assets from the same policy (#…
Browse files Browse the repository at this point in the history
…376)

* Fixed tx imbalance when burning multiple tokens

* Added cases and format

* Added unit test

* Correct unit test

* Removed redundant filtering zero-quantity asset

* Improved name function (unit test)

* Improved unit test

* Fix __iadd__ in assets

---------

Co-authored-by: Niels <[email protected]>
Co-authored-by: Jerry <[email protected]>
  • Loading branch information
3 people authored Sep 22, 2024
1 parent 46c453d commit ba73b10
Show file tree
Hide file tree
Showing 3 changed files with 39 additions and 39 deletions.
4 changes: 2 additions & 2 deletions pycardano/transaction.py
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ def __add__(self, other: Asset) -> Asset:

def __iadd__(self, other: Asset) -> Asset:
new_item = self + other
self.update(new_item)
self.data = new_item.data
return self.normalize()

def __sub__(self, other: Asset) -> Asset:
Expand Down Expand Up @@ -173,7 +173,7 @@ def __add__(self, other):

def __iadd__(self, other):
new_item = self + other
self.update(new_item)
self.data = new_item.data
return self.normalize()

def __sub__(self, other: MultiAsset) -> MultiAsset:
Expand Down
2 changes: 2 additions & 0 deletions pycardano/txbuilder.py
Original file line number Diff line number Diff line change
Expand Up @@ -556,8 +556,10 @@ def _calc_change(
provided = Value()
for i in inputs:
provided += i.output.amount

if self.mint:
provided.multi_asset += self.mint

if self.withdrawals:
for v in self.withdrawals.values():
provided.coin += v
Expand Down
72 changes: 35 additions & 37 deletions test/pycardano/test_txbuilder.py
Original file line number Diff line number Diff line change
Expand Up @@ -1943,28 +1943,23 @@ def test_transaction_witness_set_no_redeemers(chain_context):
assert witness_set.redeemer is None


def test_minting_and_burning_zero_quantity_assets(chain_context):
def test_burning_all_assets_under_single_policy(chain_context):
"""
Test the minting and burning of multiple assets using the TransactionBuilder.
Test burning all assets under a single policy with TransactionBuilder.
This test ensures that assets are correctly minted and burned under the same policy ID.
Specifically, it verifies that after burning certain assets (AssetName1, AssetName2, and AssetName3),
they are removed from the multi-asset map, and the correct amount of the minted asset (AssetName4) remains.
This test ensures that burning multiple assets (AssetName1, AssetName2, AssetName3, AssetName4)
under policy_id_1 removes them from the multi-asset map.
Steps:
1. Define a policy ID and several assets (AssetName1, AssetName2, AssetName3, and AssetName4) using the AssetName class.
2. Simulate minting of 2 units of AssetName4 and burning 1 unit each of AssetName1, AssetName2, and AssetName3.
3. Add corresponding UTXOs for each asset as inputs.
4. Add minting instructions to the TransactionBuilder.
5. Build the transaction and verify that the burnt assets are removed from the multi-asset map.
6. Check that the correct quantity of AssetName4 is minted and included in the transaction outputs.
1. Define assets under policy_id_1 and simulate burning 1 unit of each.
2. Add UTXOs for the assets and burning instructions.
3. Build the transaction and verify that all burned assets are removed.
Args:
chain_context: The blockchain context used for constructing and verifying the transaction.
chain_context: The blockchain context.
Assertions:
- AssetName1, AssetName2, and AssetName3 are not present in the multi-asset map after burning.
- AssetName4 has exactly 2 units minted.
- AssetName1, AssetName2, AssetName3, and AssetName4 are removed after burning.
"""
tx_builder = TransactionBuilder(chain_context)

Expand All @@ -1986,43 +1981,48 @@ def test_minting_and_burning_zero_quantity_assets(chain_context):
["d6cbe6cadecd3f89b60e08e68e5e6c7d72d730aaa1ad21431590f7e6643438ef", 3]
)
# Define a policy ID and asset names
policy_id = plutus_script_hash(PlutusV1Script(b"dummy script"))
multi_asset1 = MultiAsset.from_primitive({policy_id.payload: {b"AssetName1": 1}})
multi_asset2 = MultiAsset.from_primitive({policy_id.payload: {b"AssetName2": 2}})
multi_asset3 = MultiAsset.from_primitive({policy_id.payload: {b"AssetName3": 1}})
multi_asset4 = MultiAsset.from_primitive({policy_id.payload: {b"AssetName4": 3}})
policy_id_1 = plutus_script_hash(PlutusV1Script(b"dummy script1"))
multi_asset1 = MultiAsset.from_primitive({policy_id_1.payload: {b"AssetName1": 1}})
multi_asset2 = MultiAsset.from_primitive({policy_id_1.payload: {b"AssetName2": 1}})
multi_asset3 = MultiAsset.from_primitive(
{
policy_id_1.payload: {b"AssetName3": 1},
}
)
multi_asset4 = MultiAsset.from_primitive(
{
policy_id_1.payload: {b"AssetName4": 1},
}
)

# Simulate minting and burning of assets
mint = MultiAsset.from_primitive(
{
policy_id.payload: {
policy_id_1.payload: {
b"AssetName1": -1,
b"AssetName2": -2,
b"AssetName2": -1,
b"AssetName3": -1,
b"AssetName4": 2,
b"AssetName4": -1,
}
}
)

# Set UTXO for the inputs
utxo1 = UTxO(
tx_in1, TransactionOutput(Address(policy_id), Value(10000000, multi_asset1))
tx_in1, TransactionOutput(Address(policy_id_1), Value(10000000, multi_asset1))
)
utxo2 = UTxO(
tx_in2, TransactionOutput(Address(policy_id), Value(10000000, multi_asset2))
tx_in2, TransactionOutput(Address(policy_id_1), Value(10000000, multi_asset2))
)
utxo3 = UTxO(
tx_in3, TransactionOutput(Address(policy_id), Value(10000000, multi_asset3))
tx_in3, TransactionOutput(Address(policy_id_1), Value(10000000, multi_asset3))
)
utxo4 = UTxO(
tx_in4, TransactionOutput(Address(policy_id), Value(10000000, multi_asset4))
tx_in4, TransactionOutput(Address(policy_id_1), Value(10000000, multi_asset4))
)

# Add UTXO inputs
tx_builder.add_input(utxo1)
tx_builder.add_input(utxo2)
tx_builder.add_input(utxo3)
tx_builder.add_input(utxo4)
tx_builder.add_input(utxo1).add_input(utxo2).add_input(utxo3).add_input(utxo4)

# Add the minting to the builder
tx_builder.mint = mint
Expand All @@ -2037,10 +2037,8 @@ def test_minting_and_burning_zero_quantity_assets(chain_context):
for output in tx.outputs:
multi_asset = output.amount.multi_asset

# Ensure that AssetName1, Node2, and Node3 were burnt (removed)
assert AssetName(b"AssetName1") not in multi_asset.get(policy_id, {})
assert AssetName(b"AssetName2") not in multi_asset.get(policy_id, {})
assert AssetName(b"AssetName3") not in multi_asset.get(policy_id, {})

# Ensure that AssetName4 has 5 units after minting
assert multi_asset.get(policy_id, {}).get(AssetName(b"AssetName4"), 0) == 5
# Ensure that AssetName1, AssetName2, AssetName3 and AssetName4 were burnt (removed)
assert AssetName(b"AssetName1") not in multi_asset.get(policy_id_1, {})
assert AssetName(b"AssetName2") not in multi_asset.get(policy_id_1, {})
assert AssetName(b"AssetName3") not in multi_asset.get(policy_id_1, {})
assert AssetName(b"AseetName4") not in multi_asset.get(policy_id_1, {})

0 comments on commit ba73b10

Please sign in to comment.