Skip to content

Burning a Ricardian parent release can block resale of existing Ricardian-required activations #35

@Shawnleeeeee

Description

@Shawnleeeeee

Burning a Ricardian parent release can block resale of existing Ricardian-required activations

Summary

For manually created Ricardian-required activations, ActivateToken stores a Ricardian parent hash and later calls CreatorToken.creatorHasChildOf(to, parentHash) during third-party transfers/resales.

If the product creator burns the parent CreatorToken after issuing activations, CreatorToken._burn clears the global HashToRelease[parentHash] lookup. After that, creatorHasChildOf reverts with EntityID zero, so the official ProductActivate.activateTransfer resale path fails even when the resale buyer owns a valid child agreement token.

This is a conservative resale-rights / official resale DoS. I am not claiming direct fund theft or a total ERC721 lock, because the activation owner can still perform owner-initiated direct transfers. The affected path is the project-managed resale/payment flow and Ricardian-gated third-party transfer.

Affected code

  • CreatorToken._burn clears HashToRelease[Releases[tokenId].hash] and release metadata.
  • CreatorToken.creatorHasChildOf depends on creatorReleaseHashDetails(parentHash) resolving a nonzero parent entity.
  • ActivateToken._beforeTokenTransfer calls creatorHasChildOf(to, TokenIdToRicardianParent[tokenId]) for non-owner transfers.
  • ProductActivate.activateTransfer uses ActivateToken.safeTransferFrom, so the resale flow reaches that hook.

Impact

A validated seller/creator can issue a Ricardian-required activation, transfer it to a buyer, then burn the parent release. Existing buyers who list the activation for resale can no longer complete the official resale to a qualified buyer, even if that buyer owns a valid child agreement token.

The PoC shows:

  • both the original buyer and the resale buyer own valid child agreements before the burn;
  • the activation is created with RicardianReqFlag and the parent hash;
  • the buyer lists and approves the activation for resale;
  • after the seller burns the parent release, creatorHasChildOf(qualifiedBuyer, parentHash) reverts;
  • activateTransfer also reverts, leaving the activation listed but unsold.

Reproduction

PoC file:

test/CreatorTokenBurnedRicardianParentResaleDoSPoC.js

Command:

npx truffle test test\CreatorTokenBurnedRicardianParentResaleDoSPoC.js --migrate-none

Result:

Contract: CreatorToken burned Ricardian parent resale DoS PoC
  PASS lets the creator burn the Ricardian parent and block resale to a qualified buyer (2527ms)

1 passing (3s)

Why this is not just seller self-harm

The seller controls the parent release, but the denial happens after activation rights are already held by a buyer. The buyer's official resale/payment flow is broken by later global lookup deletion, even though the resale recipient satisfies the child-agreement ownership condition before the burn.

This differs from normal creator-controlled metadata changes because the activation token keeps referencing the parent hash as an authorization dependency, while CreatorToken._burn removes the lookup needed to evaluate that dependency for already-issued activations.

Suggested fix

Do not delete Ricardian relationship metadata that active activations may depend on. Possible fixes include:

  • preserve enough tombstone metadata after burn for creatorHasChildOf / creatorParentOf to keep validating existing child relationships;
  • prohibit burning a parent release that has child releases;
  • store immutable parent relationship data for Ricardian-required activations so later release burns cannot invalidate existing activation transfer checks.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions