Asset Listing Implementation Design ===== <style>.markdown-body { max-width: 1500px; }</style> Sequence diagram ---- ```mermaid sequenceDiagram title: Asset Listing #------- actor MarketplaceOwner actor AssetProvider actor Sponsor # actor Buyer #------- participant Blockchain participant MKPContract participant ISaleStrategy # participant SDK participant TimeOracle note left of MKPContract: B2B MarketplaceOwner-)Blockchain: deploy ISaleStrategy contract MarketplaceOwner->>MKPContract: whitelist ISaleStrategy note left of MKPContract: B2C note over MKPContract: req: Asset is in Asset Registered state AssetProvider-)MKPContract: list asset for sale ## Asset check MKPContract->>MKPContract: get Asset MKPContract->>MKPContract: verify AssetState alt Asset not found MKPContract--)AssetProvider: reject end alt Asset not in AssetRegistered state MKPContract--)AssetProvider: reject end MKPContract->>MKPContract: verify asset owner alt Asset not owned by caller MKPContract--)AssetProvider: reject end alt Asset has sponsor MKPContract-)Sponsor: get sponsorship Sponsor--)MKPContract: fee % end ## Listing check MKPContract->>MKPContract: verify ListingState alt Listing not in InitialState state MKPContract-->>AssetProvider: reject end ## Sale Strategy check MKPContract->>MKPContract: verify ISaleStrategy is whitelisted alt ISaleStrategy not whitelisted MKPContract-->>AssetProvider: reject end MKPContract->>ISaleStrategy: verify parameters (price, currency, etc.) alt Listing not compatible with ISaleStrategy ISaleStrategy-->>AssetProvider: reject end ## Asset Transfer MKPContract->>MKPContract: transfer token to MKPContract alt transfer not allowed MKPContract--)AssetProvider: reject end alt sponsorship request MKPContract-)Sponsor: request sponsorship alt approved Sponsor--)MKPContract: activateListing alt has pending activation MKPContract->>MKPContract: set state to PendingActivation end end alt rejected MKPContract->>MKPContract: set statet to ListingRegistered end end MKPContract->>MKPContract: calculate fee (marketplace + sponsor) ## State change MKPContract->>MKPContract: check startTime <= time now MKPContract->>MKPContract: activateListing (set state to ListingActive) alt timed activation (start time > time now) MKPContract-)TimeOracle: set execution of activateListing TimeOracle--)MKPContract: activateListing alt is in pending sponsorship state TimeOracle--)MKPContract: set isPreactevated = true end end ``` Class diagram ---- ```mermaid classDiagram class AssetState { <<enum>> InitialState PendingValidation ReadyToRegister AssetInvalidated AssetRegistered PendingSponsorship } class ListingState { <<enum>> InitialState ListingCreated PendinActivation ListingActive PendingPurchase } class AssetValidationRecord{ <<struct>> address validator address deletage bool approved } class Fee { <<struct>> uint8 percentage uint256 setAmount } class ISaleStrategy { <<interface>> +verifySaleParams(uiun256 price, bytes32 endBy) } class Listing { <<struct>> ISaleStrategy saleStrategy ListingState state bytes32 assetId addres->Fee fees address => uint256 currencyToPrice ? uint256 priceFiat ? } class Asset { <<struct>> AssetValidationRecord[] validatorHistory AssetState state bytes32 subdomain address assetContract uint256 assetId ISponsor sponsor } class AssetManagement { <<abstract>> -uint256 assetCount -address->uint256->bytes32 tokenToTokenIdToAssetId -bytes32->Asset assetById -uint256 listingCount -bytes32->Listing listingIdToListing +registerAsset(address tokenAddr, uint256 tokenId, bytes32 assetId) +listAsset(bytes32 assetId, uint256 price) +renewListing(uint256 listingId) +getAssetById(bytes32 assetId): Asset +getAssetByToken(address tokenAddr, uint256 tokenId): Asset +getLisingById() } AssetState --* Asset AssetValidationRecord --o Asset ListingState --* Listing Asset o--AssetManagement Listing o-- AssetManagement Listing *-- Fee Listing *-- ISaleStrategy ``` Example code for creating new asset and new listing ---- ```solidity= class AssettManager { registerAsset(address tokenAddr, uint256 tokenId, assetId) { ... ... bytes32 existingId = assetId == 0x0 ? tokenToTokenIdToAssetId[tokenAddr, tokenId] : assetId; bytes32 assetId = existingId == 0x0 ? keccak256(abi.encodePacked(tokenAddr, tokenId)) : existingId; Asset asset = assetById[assetId]; asset.name = name; ... } listAsset(bytes32 assetId, uint256 price) { ... ... Asset asset = assetById[assetId]; require(asset.state == AssetState.AssetRegistered); bytes32 listingId = keccak256(abi.encodePacked(asset, price)); // the asset might need unpacking first listing = listingIdToListing[listingId]; listing.price = price; listing.assetId = assetId; listing.stategy = strategy; ... emit ListingCreated(...); ... } } ``` State Flow Diagram ---- ```mermaid stateDiagram-v2 direction LR [*] --> InitialState InitialState --> ListingCreated: listAsset ListingCreated --> PendingSponsorship: requestSponsorship ListingCreated --> ListingActive: activateListing PendingSponsorship --> ListingCreated: rejectSponsorship state timed_fork <<choice>> PendingSponsorship --> timed_fork: approveSponsorship PendingSponsorship --> PendingSponsorship: activateListing timed_fork --> ListingActive: isPreactivated timed_fork --> PendingActivation: !isPreactivated ListingCreated --> PendingActivation: setActivationTimeout PendingActivation --> ListingActive: activateListing PendingActivation --> PendingSponsorship: requestSponsorship ListingActive --> ListingCreated: deactivateListing ListingActive --> InitialState: removeListing ListingActive --> PendingPurchase: lockListingForPurchase PendingPurchase --> InitialState: removeListing ListingActive --> ListingActive: changeBasePrice ListingActive --> ListingActive: changeExpiration ``` ``` Scene 1: Asset Registration with sponsorship 1. @requestSponsorship { Asset:InitialState -> Asset:PendingSponsorship } 2. @rejectSponsorship { Asset:PendingSponsorship -> Asset:SponsorshipRejected } 2. @approveSponsorship { Asset:PendingSponsorship -> Asset:Active } no listing change Scene 2: requesting sponsorship only when Asset::Active 1. @requestSponsorship { Asset::Active -> Asset:PendingSponsorship } 2. @rejectSponsorship { Asset:PendingSponsorship -> Asset:SponsorshipRejected } 2. @approveSponsorship { Asset:PendingSponsorship -> Asset:Active } no listing change Scene 3: Listing Creation with sponsorship, instant 1. @requestSponsorship { Asset::InitialState -> Asset::PendingSponsorship, Listing::InitialState -> Listing::PendingActivation } 2. @rejectSponsorship { Asset::PendingSponsorship -> Asset::SponsorshipRejected, Listing::PendingActivation -> Listing::InitialState } 3. @approveSponsorship { Asset::PendingSponsorship -> Asset::Active, Listing::PendingActivation -> Listing::Active } Scene 4: Listing Creation with sponsorship, timed 1. @requestSponsorship { Asset::InitialState -> Asset::PendingSponsorship, Listing::InitialState -> Listing::PendingActivation } a: response.time < activation.time 2a. @rejectSponsorship { Asset::PendingSponsorship -> Asset::SponsorshipRejected, Listing::PendingActivation -> Listing::InitialState } 3a. @approveSponsorship { Asset::PendingSponsorship -> Asset::Active, Listing::PendingActivation -> Listing::Active } b: response.time < activation.time 2b. @rejectSponsorship { Asset::PendingSponsorship -> Asset::SponsorshipRejected, Listing::PendingActivation -> Listing::InitialState } 3b. @approveSponsorship { Asset::PendingSponsorship -> Asset::Active, Listing::PendingActivation -> Listing::Active } Scene 5: requesting sponsorship only when Lising::Active 1. @requestSponsorship --> Scene 3 ``` The feature/flow in question has two parts, but basically bois down to requesting a sponsorship after an asset has been registered. That is, the ability to request sponsorship 1. when an asset has been registered 2. as a part of the process that creates a listing 4. when listing is created, but pending activation 5. when listing is active ```mermaid stateDiagram-v2 direction LR [*] --> Asset [*] --> Listing state Asset { [*] --> AssetInitial AssetInitial --> PendingSponsorship: requestSponsorship PendingSponsorship --> AssetActive: approveSponsorship PendingSponsorship --> SponsorshipRejected: rejectSponsorship AssetActive --> PendingSponsorship: requestSponsorship SponsorshipRejected --> PendingSponsorship: requestSponsorship } state Listing { [*] --> ListingInitial ListingInitial --> PendingActivation: requestSponsorship AssetActive --> PendingSponsorship: requestSponsorship PendingActivation --> rejectSponsorship: rejectSponsorship PendingActivation --> rejectSponsorship: rejectSponsorship } ```