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
}
```