Skip to content

Commit

Permalink
rename contract (#5)
Browse files Browse the repository at this point in the history
  • Loading branch information
austinkline authored Dec 16, 2023
1 parent 352fb54 commit e283c81
Show file tree
Hide file tree
Showing 16 changed files with 118 additions and 54 deletions.
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,5 @@
# flow-raffle
# Flowty Raffles

This repo contains an implementation of on-chain raffles on the [Flow Blockchain](https://developers.flow.com/). It makes use of
[on-chain randomness](https://developers.flow.com/build/advanced-concepts/randomness) and an array to select a random item and output it as an
event for everyone to see, making the outcome of a drawing verifiable for anyone to see.
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import "Raffles"
import "FlowtyRaffles"

pub contract RaffleSources {
pub resource GenericRaffleSource: Raffles.RaffleSource {
pub contract FlowtyRaffleSource {
pub resource GenericRaffleSource: FlowtyRaffles.RaffleSource {
pub let entries: [AnyStruct]
pub let entryType: Type

Expand Down
64 changes: 62 additions & 2 deletions contracts/Raffles.cdc → contracts/FlowtyRaffles.cdc
Original file line number Diff line number Diff line change
@@ -1,12 +1,33 @@
import "MetadataViews"

pub contract Raffles {
/*
FlowtyRaffles - The main contract which contains a definition for:
1. Raffles - A resource which encapsulates an ongoing raffle
2. RaffleSource - A resource interface which specified how a raffle will attempt to select a winner
3. Manager - A container resource for raffles to be stored in
The Raffles resource takes no view on what kind of item should be randomly drawn. Because of that, there is no
mechanism to distribute prizes when a drawing is made. This is intentional, and is meant to separate the act of
drawing an item from a raffle source, and sending it so that there are no restrictions on what kind of item might
need to be randomly drawn.
For example, a raffle could be made which draws a random post from a social media platform. In that case, only the
post itself needs to be randomly drawn. Action based on that drawing would likely need to be done separately.
Similarly one could make a raffle which distributes prizes in real-time by drawing a winner and then could:
1. Distribute the prize in a second operation in the same transaction
2. Send the item to lost and found for the winner to redeem (https://github.com/Flowtyio/lost-and-found)
*/
pub contract FlowtyRaffles {
pub let ManagerStoragePath: StoragePath
pub let ManagerPublicPath: PublicPath

pub event RaffleCreated(address: Address?, raffleID: UInt64, sourceType: Type)
pub event RaffleDrawn(address: Address?, raffleID: UInt64, sourceType: Type, index: Int, value: String, valueType: Type)

// Details - Info about the raffle. This currently includes when the raffle starts, ends, and how to display it.
pub struct Details {
pub let start: UInt64?
pub let end: UInt64?
Expand All @@ -24,6 +45,9 @@ pub contract Raffles {
}
}

// DrawingSelection - Returned when a raffle is drawn from. We will return the index that was selected and the
// value underneath it. This should assist with letting anyone using raffles to take action on them in the same transaction
// without the raffle itself needing to worry about those details.
pub struct DrawingSelection {
pub let index: Int
pub let value: AnyStruct
Expand All @@ -35,22 +59,58 @@ pub contract Raffles {
}

pub resource interface RafflePublic {
// Return the value of a raffle source at a given index
pub fun getEntryAt(index: Int): AnyStruct

// Return the details associated with this raffle
pub fun getDetails(): Details

// Return the number of entries in this raffle
pub fun getNumEntries(): Int

// Return all entries in this raffle
// NOTE: If there are too many entries in a raffle, this method will exceed computation limits
pub fun getEntries(): [AnyStruct]

// Draws a random item from the RaffleSource and returns the index that was selected along with the
// value of the item underneath.
pub fun draw(): DrawingSelection
}

pub resource interface RaffleSource {
// Should return the entry of a raffle source at a given index.
// NOTE: There is no way to enforce this on this contract, whatever RaffleSource resource
// implementation you use, make sure you trust how it performs its drawing
pub fun getEntryAt(index: Int): AnyStruct

// Adds an entry to this RaffleSource resource
// NOTE: Some raffle sources might not permit this action. For instance, using a FLOAT
// as a raffle source would mean the only way to add an entry is to mint the FLOAT
// a raffle corresponds to
pub fun addEntry(_ v: AnyStruct)

// Adds many entries at once to a given raffle source.
// NOTE: Some raffle sources might not permit this action. For instance, using a FLOAT
// as a raffle source would mean the only way to add an entry is to mint the FLOAT
// a raffle corresponds to
pub fun addEntries(_ v: [AnyStruct])

// Should return the number of entries on a RaffleSource resource.
// NOTE: There is no way to enforce this on this contract, whatever RaffleSource resource
// implementation you use, make sure you trust how it performs its drawing
pub fun getNumEntries(): Int

// Should return all entries in a RaffleSource resource
// NOTE: There is no way to enforce this on this contract, whatever RaffleSource resource
// implementation you use, make sure you trust how it performs its drawing
pub fun getEntries(): [AnyStruct]
}

pub resource Raffle: RafflePublic, MetadataViews.Resolver {
// The source for drawing winners in a raffle. Anyone can implement their own version of a RaffleSource,
// or they can use the GenericRaffleSource implementation found in FlowtyRaffleSource which can handle any
// primitive type found in cadence documentation here:
//
pub let source: @{RaffleSource}
pub let details: Details

Expand All @@ -72,7 +132,7 @@ pub contract Raffles {
let index = Int(r % UInt64(numEntries))
let value = self.source.getEntryAt(index: index)

Raffles.emitDrawing(self.owner?.address, self.uuid, self.source.getType(), index, value)
FlowtyRaffles.emitDrawing(self.owner?.address, self.uuid, self.source.getType(), index, value)
return DrawingSelection(index, value)
}

Expand Down
10 changes: 5 additions & 5 deletions flow.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,14 @@
}
},
"contracts": {
"Raffles": {
"source": "./contracts/Raffles.cdc",
"FlowtyRaffles": {
"source": "./contracts/FlowtyRaffles.cdc",
"aliases": {
"testing": "0x0000000000000007"
}
},
"RaffleSources": {
"source": "./contracts/RaffleSources.cdc",
"FlowtyRaffleSource": {
"source": "./contracts/FlowtyRaffleSource.cdc",
"aliases": {
"testing": "0x0000000000000008"
}
Expand Down Expand Up @@ -105,7 +105,7 @@
"MetadataViews",
"ViewResolver",
"NonFungibleToken",
"Raffles"
"FlowtyRaffles"
],
"emulator-ft": [
"FungibleToken",
Expand Down
4 changes: 2 additions & 2 deletions scripts/borrow_manager.cdc
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import "Raffles"
import "FlowtyRaffles"

pub fun main(addr: Address) {
getAccount(addr).getCapability<&Raffles.Manager{Raffles.ManagerPublic}>(Raffles.ManagerPublicPath).borrow()
getAccount(addr).getCapability<&FlowtyRaffles.Manager{FlowtyRaffles.ManagerPublic}>(FlowtyRaffles.ManagerPublicPath).borrow()
?? panic("unable to borrow manager")
}
4 changes: 2 additions & 2 deletions scripts/get_num_raffle_entries.cdc
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import "Raffles"
import "FlowtyRaffles"

pub fun main(addr: Address, id: UInt64): Int {
let acct = getAuthAccount(addr)
let manager = acct.borrow<&Raffles.Manager{Raffles.ManagerPublic}>(from: Raffles.ManagerStoragePath)
let manager = acct.borrow<&FlowtyRaffles.Manager{FlowtyRaffles.ManagerPublic}>(from: FlowtyRaffles.ManagerStoragePath)
?? panic("raffles manager not found")
let raffle = manager.borrowRafflePublic(id: id)
?? panic("raffle not found")
Expand Down
6 changes: 3 additions & 3 deletions scripts/get_raffle_details.cdc
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import "Raffles"
import "FlowtyRaffles"

pub fun main(addr: Address, id: UInt64): Raffles.Details? {
pub fun main(addr: Address, id: UInt64): FlowtyRaffles.Details? {
let acct = getAuthAccount(addr)
let manager = acct.borrow<&Raffles.Manager>(from: Raffles.ManagerStoragePath)
let manager = acct.borrow<&FlowtyRaffles.Manager>(from: FlowtyRaffles.ManagerStoragePath)
?? panic("raffles manager not found")

if let raffle = manager.borrowRafflePublic(id: id) {
Expand Down
4 changes: 2 additions & 2 deletions scripts/get_raffle_entries.cdc
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import "Raffles"
import "FlowtyRaffles"

pub fun main(addr: Address, id: UInt64): [AnyStruct] {
let acct = getAuthAccount(addr)
let manager = acct.borrow<&Raffles.Manager{Raffles.ManagerPublic}>(from: Raffles.ManagerStoragePath)
let manager = acct.borrow<&FlowtyRaffles.Manager{FlowtyRaffles.ManagerPublic}>(from: FlowtyRaffles.ManagerStoragePath)
?? panic("raffles manager not found")
let raffle = manager.borrowRafflePublic(id: id)
?? panic("raffle not found")
Expand Down
4 changes: 2 additions & 2 deletions scripts/get_raffle_source_identifier.cdc
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import "Raffles"
import "FlowtyRaffles"

pub fun main(addr: Address, path: StoragePath): String {
let acct = getAuthAccount(addr)
let source = acct.borrow<&{Raffles.RaffleSource}>(from: path)
let source = acct.borrow<&{FlowtyRaffles.RaffleSource}>(from: path)
return source!.getType().identifier
}
24 changes: 12 additions & 12 deletions test/Raffles_tests.cdc → test/FlowtyRaffles_tests.cdc
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
import Test
import "test_helpers.cdc"

import "Raffles"
import "RaffleSources"
import "FlowtyRaffles"
import "FlowtyRaffleSource"
import "MetadataViews"

pub let RafflesContractAddress = Address(0x0000000000000007)
pub let RaffleSourcesContractAddress = Address(0x0000000000000008)
pub let FlowtyRafflesContractAddress = Address(0x0000000000000007)
pub let FlowtyRaffleSourceContractAddress = Address(0x0000000000000008)

pub let GenericRaffleSourceIdentifier = "A.0000000000000008.RaffleSources.GenericRaffleSource"
pub let GenericRaffleSourceIdentifier = "A.0000000000000008.FlowtyRaffleSource.GenericRaffleSource"

pub fun setup() {
var err = Test.deployContract(name: "Raffles", path: "../contracts/Raffles.cdc", arguments: [])
var err = Test.deployContract(name: "FlowtyRaffles", path: "../contracts/FlowtyRaffles.cdc", arguments: [])
Test.expect(err, Test.beNil())

err = Test.deployContract(name: "RaffleSources", path: "../contracts/RaffleSources.cdc", arguments: [])
err = Test.deployContract(name: "FlowtyRaffleSource", path: "../contracts/FlowtyRaffleSource.cdc", arguments: [])
Test.expect(err, Test.beNil())
}

Expand All @@ -41,7 +41,7 @@ pub fun testCreateRaffle() {
let end: UInt64? = nil

txExecutor("create_raffle.cdc", [acct], [Type<Address>(), start, end, name, description, thumbnail], nil)
let createEvent = (Test.eventsOfType(Type<Raffles.RaffleCreated>()).removeLast() as! Raffles.RaffleCreated)
let createEvent = (Test.eventsOfType(Type<FlowtyRaffles.RaffleCreated>()).removeLast() as! FlowtyRaffles.RaffleCreated)
assert(acct.address == createEvent.address)

let details = getRaffleDetails(acct, createEvent.raffleID) ?? panic("raffle not found")
Expand Down Expand Up @@ -93,9 +93,9 @@ pub fun testDrawFromRaffle() {
assert(accounts[drawing2] != nil)
}

pub fun getRaffleDetails(_ acct: Test.Account, _ id: UInt64): Raffles.Details? {
pub fun getRaffleDetails(_ acct: Test.Account, _ id: UInt64): FlowtyRaffles.Details? {
if let res = scriptExecutor("get_raffle_details.cdc", [acct.address, id]) {
return res as! Raffles.Details
return res as! FlowtyRaffles.Details
}
return nil
}
Expand Down Expand Up @@ -130,13 +130,13 @@ pub fun createAddressRaffle(_ acct: Test.Account): UInt64 {
let end: UInt64? = nil

txExecutor("create_raffle.cdc", [acct], [Type<Address>(), start, end, name, description, thumbnail], nil)
let createEvent = (Test.eventsOfType(Type<Raffles.RaffleCreated>()).removeLast() as! Raffles.RaffleCreated)
let createEvent = (Test.eventsOfType(Type<FlowtyRaffles.RaffleCreated>()).removeLast() as! FlowtyRaffles.RaffleCreated)
return createEvent.raffleID
}

pub fun drawFromRaffle(_ signer: Test.Account, _ addr: Address, _ id: UInt64): String {
txExecutor("draw_from_raffle.cdc", [signer], [addr, id], nil)

let drawingEvent = Test.eventsOfType(Type<Raffles.RaffleDrawn>()).removeLast() as! Raffles.RaffleDrawn
let drawingEvent = Test.eventsOfType(Type<FlowtyRaffles.RaffleDrawn>()).removeLast() as! FlowtyRaffles.RaffleDrawn
return drawingEvent.value
}
4 changes: 2 additions & 2 deletions transactions/add_entries_to_raffle.cdc
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import "Raffles"
import "FlowtyRaffles"

transaction(raffleID: UInt64, entries: [AnyStruct]) {
prepare(acct: AuthAccount) {
let manager = acct.borrow<&Raffles.Manager>(from: Raffles.ManagerStoragePath)
let manager = acct.borrow<&FlowtyRaffles.Manager>(from: FlowtyRaffles.ManagerStoragePath)
?? panic("raffles manager not found")
let raffle = manager.borrowRaffle(id: raffleID)
?? panic("raffle not found")
Expand Down
4 changes: 2 additions & 2 deletions transactions/add_entry_to_raffle.cdc
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import "Raffles"
import "FlowtyRaffles"

transaction(raffleID: UInt64, entry: AnyStruct) {
prepare(acct: AuthAccount) {
let manager = acct.borrow<&Raffles.Manager>(from: Raffles.ManagerStoragePath)
let manager = acct.borrow<&FlowtyRaffles.Manager>(from: FlowtyRaffles.ManagerStoragePath)
?? panic("raffles manager not found")
let raffle = manager.borrowRaffle(id: raffleID)
?? panic("raffle not found")
Expand Down
16 changes: 8 additions & 8 deletions transactions/create_raffle.cdc
Original file line number Diff line number Diff line change
@@ -1,25 +1,25 @@
import "Raffles"
import "RaffleSources"
import "FlowtyRaffles"
import "FlowtyRaffleSource"
import "MetadataViews"

transaction(type: Type, start: UInt64?, end: UInt64?, name: String, description: String, thumbnail: String) {
prepare(acct: AuthAccount) {
let source <- RaffleSources.createRaffleSource(type)
let source <- FlowtyRaffleSource.createRaffleSource(type)

if acct.borrow<&AnyResource>(from: Raffles.ManagerStoragePath) == nil {
acct.save(<-Raffles.createManager(), to: Raffles.ManagerStoragePath)
acct.link<&Raffles.Manager{Raffles.ManagerPublic}>(Raffles.ManagerPublicPath, target: Raffles.ManagerStoragePath)
if acct.borrow<&AnyResource>(from: FlowtyRaffles.ManagerStoragePath) == nil {
acct.save(<-FlowtyRaffles.createManager(), to: FlowtyRaffles.ManagerStoragePath)
acct.link<&FlowtyRaffles.Manager{FlowtyRaffles.ManagerPublic}>(FlowtyRaffles.ManagerPublicPath, target: FlowtyRaffles.ManagerStoragePath)
}

let manager = acct.borrow<&Raffles.Manager>(from: Raffles.ManagerStoragePath)
let manager = acct.borrow<&FlowtyRaffles.Manager>(from: FlowtyRaffles.ManagerStoragePath)
?? panic("raffles manager not found")

let display = MetadataViews.Display(
name: name,
description: description,
thumbnail: MetadataViews.HTTPFile(thumbnail)
)
let details = Raffles.Details(start, end, display)
let details = FlowtyRaffles.Details(start, end, display)
let id = manager.createRaffle(source: <-source, details: details)

// make sure you can borrow the raffle back
Expand Down
4 changes: 2 additions & 2 deletions transactions/create_raffle_source.cdc
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import "RaffleSources"
import "FlowtyRaffleSource"

transaction(type: Type, path: StoragePath) {
prepare(acct: AuthAccount) {
let source <- RaffleSources.createRaffleSource(type)
let source <- FlowtyRaffleSource.createRaffleSource(type)
let t = source.getEntryType()
assert(t == type)

Expand Down
4 changes: 2 additions & 2 deletions transactions/draw_from_raffle.cdc
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import "Raffles"
import "FlowtyRaffles"

transaction(addr: Address, id: UInt64) {
prepare(acct: AuthAccount) { }
execute {
let manager = getAccount(addr).getCapability<&Raffles.Manager{Raffles.ManagerPublic}>(Raffles.ManagerPublicPath).borrow()
let manager = getAccount(addr).getCapability<&FlowtyRaffles.Manager{FlowtyRaffles.ManagerPublic}>(FlowtyRaffles.ManagerPublicPath).borrow()
?? panic("raffles manager not found")
let raffle = manager.borrowRafflePublic(id: id)
?? panic("raffle not found")
Expand Down
8 changes: 4 additions & 4 deletions transactions/setup_manager.cdc
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import "Raffles"
import "FlowtyRaffles"

transaction {
prepare(acct: AuthAccount) {
let manager <- Raffles.createManager()
acct.save(<-manager, to: Raffles.ManagerStoragePath)
acct.link<&Raffles.Manager{Raffles.ManagerPublic}>(Raffles.ManagerPublicPath, target: Raffles.ManagerStoragePath)
let manager <- FlowtyRaffles.createManager()
acct.save(<-manager, to: FlowtyRaffles.ManagerStoragePath)
acct.link<&FlowtyRaffles.Manager{FlowtyRaffles.ManagerPublic}>(FlowtyRaffles.ManagerPublicPath, target: FlowtyRaffles.ManagerStoragePath)
}
}

0 comments on commit e283c81

Please sign in to comment.