Skip to content

Commit

Permalink
Merge pull request #1 from onflow/c1-migration
Browse files Browse the repository at this point in the history
Migrate cadence code to 1.0
  • Loading branch information
franklywatson authored Dec 4, 2024
2 parents 3a6603f + 288d7b5 commit 706e177
Show file tree
Hide file tree
Showing 16 changed files with 388 additions and 88 deletions.
51 changes: 51 additions & 0 deletions .github/workflows/cadence_lint.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
name: Run Cadence Contract Compilation, Deployment, Transaction Execution, and Lint
on: push

jobs:
run-cadence-lint:
runs-on: macos-latest
steps:
- name: Checkout
uses: actions/checkout@v3
with:
submodules: 'true'

- name: Install Flow CLI
run: |
brew update
brew install flow-cli
- name: Initialize Flow
run: |
if [ ! -f flow.json ]; then
echo "Initializing Flow project..."
flow init
else
echo "Flow project already initialized."
fi
flow dependencies install
- name: Start Flow Emulator
run: |
echo "Starting Flow emulator in the background..."
nohup flow emulator start > emulator.log 2>&1 &
sleep 5 # Wait for the emulator to start
flow project deploy --network=emulator # Deploy the recipe contracts indicated in flow.json
- name: Run All Transactions
run: |
echo "Running all transactions in the transactions folder..."
for file in ./cadence/transactions/*.cdc; do
echo "Running transaction: $file"
TRANSACTION_OUTPUT=$(flow transactions send "$file" --signer emulator-account)
echo "$TRANSACTION_OUTPUT"
if echo "$TRANSACTION_OUTPUT" | grep -q "Transaction Error"; then
echo "Transaction Error detected in $file, failing the action..."
exit 1
fi
done
- name: Run Cadence Lint
run: |
echo "Running Cadence linter on .cdc files in the current repository"
flow cadence lint ./cadence/**/*.cdc
34 changes: 34 additions & 0 deletions .github/workflows/cadence_tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
name: Run Cadence Tests
on: push

jobs:
run-cadence-tests:
runs-on: macos-latest
steps:
- name: Checkout
uses: actions/checkout@v3
with:
submodules: 'true'

- name: Install Flow CLI
run: |
brew update
brew install flow-cli
- name: Initialize Flow
run: |
if [ ! -f flow.json ]; then
echo "Initializing Flow project..."
flow init
else
echo "Flow project already initialized."
fi
- name: Run Cadence Tests
run: |
if test -f "cadence/tests.cdc"; then
echo "Running Cadence tests in the current repository"
flow test cadence/tests.cdc
else
echo "No Cadence tests found. Skipping tests."
fi
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
.DS_Store
.DS_Store
/imports/
/.idea/
60 changes: 48 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ Once you have a set created and some plays, you can use this to add a play to a
- [Description](#description)
- [What is included in this repository?](#what-is-included-in-this-repository)
- [Supported Recipe Data](#recipe-data)
-[Deploying Recipe Contracts and Running Transactions Locally (Flow Emulator)](#deploying-recipe-contracts-and-running-transactions-locally-flow-emulator)
- [License](#license)

## Description
Expand All @@ -19,7 +20,6 @@ The Cadence Cookbook is a collection of code examples, recipes, and tutorials de

Each recipe in the Cadence Cookbook is a practical coding example that showcases a specific aspect of Cadence or use-case on Flow, including smart contract development, interaction, and best practices. By following these recipes, you can gain hands-on experience and learn how to leverage Cadence for your blockchain projects.


### Contributing to the Cadence Cookbook

Learn more about the contribution process [here](https://github.com/onflow/cadence-cookbook/blob/main/contribute.md).
Expand All @@ -34,17 +34,17 @@ Recipe metadata, such as title, author, and category labels, is stored in `index

```
recipe-name/
├── cadence/ # Cadence files for recipe examples
│ ├── contract.cdc # Contract code
│ ├── transaction.cdc # Transaction code
│ ├── tests.cdc # Tests code
├── explanations/ # Explanation files for recipe examples
│ ├── contract.txt # Contract code explanation
├── transaction.txt # Transaction code explanation
├── tests.txt # Tests code explanation
├── index.js # Root file for storing recipe metadata
├── README.md # This README file
└── LICENSE # License information
├── cadence/ # Cadence files for recipe examples
│ ├── contracts/Recipe.cdc # Contract code
│ ├── transactions/create_plays.cdc # Transaction code
│ ├── tests/Recipe_test.cdc # Tests code
├── explanations/ # Explanation files for recipe examples
│ ├── contract.txt # Contract code explanation
│ ├── transaction.txt # Transaction code explanation
│ ├── tests.txt # Tests code explanation
├── index.js # Root file for storing recipe metadata
├── README.md # This README file
└── LICENSE # License information
```

## Supported Recipe Data
Expand Down Expand Up @@ -95,6 +95,42 @@ export const sampleRecipe= {
transactionExplanation: transactionExplanationPath,
};
```
## Deploying Recipe Contracts and Running Transactions Locally (Flow Emulator)

This section explains how to deploy the recipe's contracts to the Flow emulator, run the associated transaction with sample arguments, and verify the results.

### Prerequisites

Before deploying and running the recipe:

1. Install the Flow CLI. You can find installation instructions [here](https://docs.onflow.org/flow-cli/install/).
2. Ensure the Flow emulator is installed and ready to use with `flow version`.

### Step 1: Start the Flow Emulator

Start the Flow emulator to simulate the blockchain environment locally

```bash
flow emulator start
```

### Step 2: Deploy Project Contracts

Deploy contracts to the emulator. This will deploy all the contracts specified in the _deployments_ section of `flow.json` whether project contracts or dependencies

```bash
flow project deploy --network=emulator
```

### Step 3: Run the Transaction

The transaction file is located in `./cadence/transactions/create_plays.cdc`. To run the transaction, execute the following command:

```bash
flow transactions send cadence/transactions/create_plays.cdc --signer emulator-account
```

To verify the transaction's execution, check the emulator logs printed during the transaction for confirmation messages. You can add the `--log-level debug` flag to your Flow CLI command for more detailed output during contract deployment or transaction execution.

## License

Expand Down
34 changes: 0 additions & 34 deletions cadence/contract.cdc

This file was deleted.

57 changes: 57 additions & 0 deletions cadence/contracts/Recipe.cdc
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import "TopShot"

access(all) contract Recipe {
// This is a snippet extracting the relevant logic from the TopShot contract for demonstration purposes
// More TopShot Code Above
access(all) event PlayAddedToSet(setID: UInt32, playID: UInt32)

access(all) resource Set {

// addPlay adds a play to the set
//
// Parameters: playID: The ID of the Play that is being added
//
// Pre-Conditions:
// The Play needs to be an existing play
// The Set needs to be not locked
// The Play can't have already been added to the Set
//
/// Resource fields
access(all) var locked: Bool
access(all) let plays: [UInt32]
access(all) let retired: {UInt32: Bool}
access(all) let numberMintedPerPlay: {UInt32: UInt32}
access(all) let setID: UInt32

// Resource initializer
init(setID: UInt32) {
self.locked = false
self.plays = []
self.retired = {}
self.numberMintedPerPlay = {}
self.setID = setID
}

access(all) fun addPlay(playID: UInt32) {
pre {
TopShot.getPlayMetaData(playID: playID) != nil: "Cannot add the Play to Set: Play doesn't exist."
!self.locked: "Cannot add the play to the Set after the set has been locked."
self.numberMintedPerPlay[playID] == nil: "The play has already been added to the set."
}

// Add the Play to the array of Plays
self.plays.append(playID)

// Open the Play up for minting
self.retired[playID] = false

// Initialize the Moment count to zero
self.numberMintedPerPlay[playID] = 0

emit PlayAddedToSet(setID: self.setID, playID: playID)
}
}
// More TopShot Code Below
}
1 change: 0 additions & 1 deletion cadence/tests.cdc → cadence/tests/Recipe_test.cdc
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,3 @@ access(all) fun testExample() {
let array = [1, 2, 3]
Test.expect(array.length, Test.equal(3))
}

27 changes: 0 additions & 27 deletions cadence/transaction.cdc

This file was deleted.

42 changes: 42 additions & 0 deletions cadence/transactions/create_plays.cdc
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import "TopShot"

transaction {

let admin: &TopShot.Admin
let borrowedSet: &TopShot.Set

prepare(acct: auth(Storage) &Account) {
// Borrow the admin resource from storage
self.admin = acct.storage.borrow<&TopShot.Admin>(from: /storage/TopShotAdmin)
?? panic("Cannot borrow admin resource")

// Ensure the Set resource exists
if acct.storage.borrow<&TopShot.Set>(from: /storage/TopShotSet) == nil {
let newSet = self.admin.createSet(name: "test_set")
acct.storage.save(newSet, to: /storage/TopShotSet)
}

// Borrow the specified Set from the admin
self.borrowedSet = self.admin.borrowSet(setID: 1)

// Create plays if they don't already exist
let playIDs: [UInt32] = [1, 2, 3]
for playID in playIDs {
if TopShot.getPlayMetaData(playID: playID) == nil {
let metadata: {String: String} = {
"Player": "Player Name ".concat(playID.toString()),
"Play": "Play Description ".concat(playID.toString())
}
self.admin.createPlay(metadata: metadata)
}
}
}

execute {
// Add plays to the set
self.borrowedSet.addPlay(playID: 1)
self.borrowedSet.addPlay(playID: 2)
self.borrowedSet.addPlay(playID: 3)
log("Play added")
}
}
1 change: 1 addition & 0 deletions contract.cdc
1 change: 1 addition & 0 deletions emulator-account.pkey
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
0xb6ebc3895e1df1de0999390bb2aa302509f256460e960ed3eed8a4d3826322d8
12 changes: 7 additions & 5 deletions explanations/contract.txt
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
Once you have a set created and you've created some plays, you can then add a play to your set by calling the addPlay function in your set.
The Recipe contract demonstrates how to interact with TopShot Sets by allowing Plays to be added to a specific Set. This functionality is essential for enabling Plays to be minted as Moments later in the lifecycle of the application. The core logic for adding Plays is implemented in the addPlay function within the Set resource.

You would need to pass in the playId's to have them available in the set. Before the function goes through it will check to see if the playID actually exists. If not you'll need to use another one, or create on.
To add a Play to a Set, one must call the addPlay function with the ID of the Play (playID) you want to include. The function ensures that certain preconditions are met before proceeding. First, it checks whether the playID exists in the TopShot contract by querying its metadata. If the Play does not exist, the function will fail, and you will need to either use an existing playID or create a new Play. This step ensures that only valid Plays are added to the Set.

It will also check if the Set has been locked, meaning no more plays can be added, or if the play has already been added to the set.
The function also verifies that the Set is not locked. A locked Set cannot have new Plays added to it, which helps maintain the integrity of finalized Sets. Additionally, the function checks whether the playID has already been added to the Set. This prevents duplicate entries, ensuring each Play is unique within the Set.

Once that check is complete, it will add the play to the array of play ID's in the set. The function will also include the play ID in a retired dictionary and set the boolean as false. The number minted per play ID will also be included in a dictionary and initialized as 0.
Once these checks are complete, the playID is added to the array of Plays (plays) associated with the Set. The function also updates a dictionary called retired, setting the status of the playID to false, meaning it is active and can be used for minting Moments. Another dictionary, numberMintedPerPlay, is updated to initialize the count of Moments minted for this Play at zero.

Finally you would emit an event that the play has been added to the set.
Finally, the addPlay function emits an event, PlayAddedToSet, which signals that a Play has been successfully added to the Set. This event includes the setID and playID and provides an auditable record of the action.

The Set resource also includes several fields that store important information. The plays array keeps track of all the Plays added to the Set. The retired dictionary indicates whether a Play has been retired (i.e., no longer available for minting Moments), while the numberMintedPerPlay dictionary tracks how many Moments have been minted for each Play in the Set. The locked field determines whether the Set can still accept new Plays, and the setID uniquely identifies the Set.
16 changes: 13 additions & 3 deletions explanations/transaction.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
To add a play to a set you will need to borrow a reference to the admin resource from the Auth Account.
This transaction focuses on adding Plays to a TopShot Set. It follows a structured process to ensure that both the Set and the Plays exist and are correctly updated.

Once you do, you will need to borrow the set that you would like access to by calling the borrowSet function. This gets whatever setID is created that you want to have access to.
First, the transaction begins by borrowing a reference to the Admin resource from the account’s storage. This resource provides administrative access to manage Sets and Plays. Without this reference, the transaction cannot proceed, as the Admin resource is essential for creating Sets or Plays.

When that happens you would just use the addPlay function to add whichever plays you have already created to this set.
Next, the transaction checks if a specific Set exists in the account’s storage. If the Set is not present, it creates a new one using the createSet function provided by the Admin resource. This ensures that there is always a valid Set available for managing Plays. Once the Set is confirmed or created, the transaction borrows a reference to it by specifying its setID.

The transaction then moves on to ensure that the Plays to be added to the Set exist. It does this by iterating through a predefined list of playIDs. For each playID, it checks if the Play's metadata exists. If the Play is missing, it creates the Play using the createPlay function, passing dynamically generated metadata such as the player’s name and play description. This step guarantees that only valid Plays are added to the Set.

Finally, the transaction uses the addPlay function on the borrowed Set to add each Play by its playID. This function performs several checks:

- It ensures the Play exists.
- It verifies the Set is not locked, meaning it can still accept new Plays.
- It checks that the Play has not already been added to the Set.

Once these conditions are met, the Play is added to the Set, and its retired status and minted count are initialized. After all Plays have been added, a success message is logged to indicate the operation has completed.
Loading

0 comments on commit 706e177

Please sign in to comment.