Chaincode
Chaincode (on other blockchain networks known as Smart Contract) is the network level code responsible for executing domain logic and storing the result on the distributed ledger.
Intro
BAM Ticketing chaincode contains functions that create so-called assets on the network. An asset could be a ticket, event, secondary market offer, or any other resource that's stored on the ledger and holds a value (i.e. a ticket bought on the primary market which allows entering the venue where the event takes place).
Every asset has the notion of ownership, whether it's the owner of a ticket or the organizer that created the event. Based on that ownership, the caller (or suitably called signer) is authorized to call specific functions on specific assets. For example, organizers enroll for a certificate that holds the permissions to create events and later make updates to the deployed event and tickets created for the event. When a user buys the ticket (via primary or secondary market), the ticket gets transferred to the user's wallet. Later on, only that specific user who owns the ticket can transfer the ticket to somebody else.
Convention
For a successful blockchain transaction, three API calls need to be done:
- POST /blockchain/v1/transaction/proposal - where the sender specifies the function and arguments and sends his proposal
- POST /blockchain/v1/transaction/proposal/{digest} - signing of the generated proposal digest
- POST /blockchain/v1/transaction/{digest} - signing of the generated transaction digest
Details on these calls are described in the transactions chapter.
Functions
Event creation
Creates event and ticket configuration assets. In case of a recurring event, the caller can send multiple event objects inside the data array. The signer must have the event.create and ticket_config.create permission for the given organizer_id inside the enrollment certificate. The tenant_name should match the name of the tenant where the blockchain service is deployed (it's used for later ticket asset creation after publishing the event on the marketplace).
fcn: EVENT.CREATE
permissions: event.create, ticket_config.create
arg:
{
"data": [
{
"id": "uint64",
"type": "recurring | single | occurrence",
"start_at": "string (RFC3339 format)",
"end_at": "string (RFC3339 format)",
"gate_time": "string (RFC3339 format)",
"ticket_config": [
{
"id": "uint64",
"event_id": "uint64",
"face_value": "decimal",
"quantity": "uint64",
"start_sale_at": "string (RFC3339 format)",
"end_sale_at": "string (RFC3339 format)",
"currency": "string",
"kyc_required": "bool?",
"secondary_market_ruleset_id": "string?"
},
...
],
"organizer_id": "string",
"tenant_name": "string"
},
...
]
}
Ticket fetching
To retrieve the ticket asset, use this method and provide the ticket asset key (composite key including the event, ticket configuration, and ticket ID).
fcn: TICKET.GET
permissions: none
arg:
{
"key": "E{event_id}TC{ticket_config_id}T{ticket_id}"
}
Ticket transfer
To transfer one or multiple tickets to another wallet, the owner of the ticket should use this function. NOTE: the seq_num field is equal to the ticket ID.
fcn: TICKET.TRANSFER
permissions: none
arg:
{
"data": [
{
"seq_num": "uint64",
"ticket_config": {
"id": "uint64",
"event": {
"id": "uint64"
}
},
"new_owner_id": "string"
},
...
]
}
NOTE: If the enable_transfer
on the ticket configs SMR is set to false
, the ticket transfer is not allowed.
Ticket invalidate
This function is used by a validator to invalidate the ticket on the venue entrance. The validation_time field is used to indicate at what point in time the ticket was validated (used when the tickets have start and end validity times, for example on multi-day event tickets). The signer must have the ticket.invalidate permission for the given organizer_id on the event.
fcn: TICKET.INVALIDATE
permissions: ticket.invalidate
arg:
{
"seq_num": "uint64",
"ticket_config": {
"id": "uint64",
"event": {
"id": "uint64"
}
},
"validation_time": "string (RFC3339 format)"
}
Ticket resale (mark for sale)
The ticket owner can put his tickets for sale on the secondary market by calling this function. This function locks the ticket for the specified period pointed with the locked_until field, so it can't be transferred, invalidate or activated until it expires. When the locked_until field isn't supplied, the chaincode locks the ticket until the event start. The user needs to specify the type of sale and the price of the ticket. Optionally, in the case of a DIRECT sale, the user can specify who can buy the ticket by sending the receiver's wallet ID in the buyer_id field.
fcn: TICKET.MARK_FOR_SALE
permissions: none
arg:
{
"tickets": [
{
"seq_num": "uint64",
"ticket_config":{
"id": "uint64",
"event": {
"id": "uint64"
}
},
"locked_until": "string? (RFC3339 format)"
"price": "decimal",
"type": ['DIRECT', 'MARKET', 'AUCTION'],
"buyer_id": "string?"
}
]
}
NOTE:
The resale is not allowed if the enable_resale
flag on the SMR of the ticket config is set to false.
The price of the ticket also depends on the SMR price limits. See Secondary Market Docs.
Ticket activation
When a user wants to lock their ticket so that it can only be invalidated at the venue entrance, they should call the ticket activate function. The caller can specify multiple tickets supplied in the data array field.
fcn: TICKET.ACTIVATE
permissions: none
arg:
{
"data": [
{
"seq_num": "uint64",
"ticket_config":{
"id": "uint64",
"event": {
"id": "uint64"
}
}
}
]
}
Creating a secondary market ruleset (SMR)
The SMR allows the organizer to control ticket sales on the secondary market. SMR can be applied to ticket configs. Each ticket config can have a different SMR applied.
fcn: SECONDARY_MARKET_RULESET.SET
permissions: SEC_MARKET_RULES_SET
arg:
{
"id": "string",
"enable_transfer": "bool?",
"enable_resale": "bool?",
"organizer_id": "string",
"tenant_name": "string",
"fees": "OrganizerFee[]",
"price_limits": "PriceLimit[]"
}
OrganizerFee
{
"type": ['PERCENTAGE', 'FIXED'],
"value": "decimal"
}
Note:
For now, fees are not yet implemented on the chaincode.
PriceLimit
{
"type": ['MAX_PERCENTAGE', 'MAX_AMOUNT'],
"value": "decimal"
}
Note:
These are the rules when marking a ticket for sale.
MAX_PERCENTAGE
means thatface_value * value >= price
MAX_AMOUNT
means thatvalue >= price
Where:
price
is the price that the user sets for the ticket on the secondary marketface_value
is the property of the ticket config associated with the ticketvalue
is the value of thePriceLimit
item