Skip to content

Commit

Permalink
Merge pull request #1 from onflow/c1-migration
Browse files Browse the repository at this point in the history
C1 migration
  • Loading branch information
franklywatson authored Dec 16, 2024
2 parents b375054 + 1203055 commit 32f39e1
Show file tree
Hide file tree
Showing 14 changed files with 389 additions and 56 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/
61 changes: 49 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ Create a new collection for an existing Flow account that doesn't have one so th
- [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_collection.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,43 @@ 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: Install Dependencies and 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 dependencies install
flow project deploy --network=emulator
```

### Step 3: Run the Transaction

Transactions associated with the recipe are located in `./cadence/transactions`. To run a transaction, execute the following command:

```bash
flow transactions send cadence/transactions/TRANSACTION_NAME.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
4 changes: 0 additions & 4 deletions cadence/contract.cdc

This file was deleted.

1 change: 1 addition & 0 deletions cadence/contract.cdc
79 changes: 79 additions & 0 deletions cadence/contracts/Recipe.cdc
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
/// ExampleNFT.cdc
///
/// This is a complete version of the ExampleNFT contract
/// that includes withdraw and deposit functionalities, as well as a
/// collection resource that can be used to bundle NFTs together.
///
/// Learn more about non-fungible tokens in this tutorial: https://developers.flow.com/cadence/tutorial/non-fungible-tokens-1
access(all) contract ExampleNFT {

// Declare Path constants
access(all) let CollectionStoragePath: StoragePath
access(all) let CollectionPublicPath: PublicPath
access(all) let MinterStoragePath: StoragePath

// Tracks the unique IDs of the NFTs
access(all) var idCount: UInt64

access(all) resource NFT {
access(all) let id: UInt64
init(initID: UInt64) {
self.id = initID
}
}

access(all) entitlement Withdraw

// Interface defining required methods for the Collection
access(all) resource interface CollectionInterface {
access(all) fun deposit(token: @NFT)
access(Withdraw) fun withdraw(withdrawID: UInt64): @NFT
access(all) view fun getIDs(): [UInt64]
}

// Collection resource conforming to CollectionInterface
access(all) resource Collection: CollectionInterface {
access(all) var ownedNFTs: @{UInt64: NFT}

init () {
self.ownedNFTs <- {}
}

access(Withdraw) fun withdraw(withdrawID: UInt64): @NFT {
let token <- self.ownedNFTs.remove(key: withdrawID)
?? panic("Could not withdraw NFT with id=".concat(withdrawID.toString()))
return <-token
}

access(all) fun deposit(token: @NFT) {
self.ownedNFTs[token.id] <-! token
}

access(all) view fun getIDs(): [UInt64] {
return self.ownedNFTs.keys
}
}

access(all) fun createEmptyCollection(): @Collection {
return <- create Collection()
}

access(all) fun mintNFT(): @NFT {
let newNFT <- create NFT(initID: self.idCount)
self.idCount = self.idCount + 1
return <-newNFT
}

init() {
self.CollectionStoragePath = /storage/nftTutorialCollection
self.CollectionPublicPath = /public/nftTutorialCollection
self.MinterStoragePath = /storage/nftTutorialMinter

self.idCount = 1

self.account.storage.save(<-self.createEmptyCollection(), to: self.CollectionStoragePath)
let cap = self.account.capabilities.storage.issue<&Collection>(self.CollectionStoragePath)
self.account.capabilities.publish(cap, at: self.CollectionPublicPath)
}
}
17 changes: 17 additions & 0 deletions cadence/tests/Recipe_test.cdc
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import Test

access(all) fun testExample() {
let array = [1, 2, 3]
Test.expect(array.length, Test.equal(3))
}

access(all)
fun setup() {
let err = Test.deployContract(
name: "ExampleNFT",
path: "../contracts/Recipe.cdc",
arguments: [],
)

Test.expect(err, Test.beNil())
}
33 changes: 0 additions & 33 deletions cadence/transaction.cdc

This file was deleted.

1 change: 1 addition & 0 deletions cadence/transaction.cdc
37 changes: 37 additions & 0 deletions cadence/transactions/create_collection.cdc
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import "NonFungibleToken"
import "ExampleNFT"
import "MetadataViews"

// This transaction is what an account would run
// to set itself up to receive NFTs
transaction {

prepare(signer: auth(Storage, Capabilities) &Account) {
// Check if the account already has a collection
let collectionRef = signer.storage.borrow<&ExampleNFT.Collection>(from: ExampleNFT.CollectionStoragePath)

// If a collection already exists, return early
if collectionRef != nil {
return
}

// Create a new empty collection
let collection <- ExampleNFT.createEmptyCollection()

// Save it to the account storage
signer.storage.save(<-collection, to: ExampleNFT.CollectionStoragePath)

// Create a capability for the collection
let collectionCap = signer.capabilities.storage.issue<&{NonFungibleToken.CollectionPublic}>(
ExampleNFT.CollectionStoragePath
)

// Publish the capability
signer.capabilities.publish(collectionCap, at: ExampleNFT.CollectionPublicPath)
}

execute {
log("Setup account")
}
}
1 change: 1 addition & 0 deletions emulator-account.pkey
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
0xdc07d83a937644ff362b279501b7f7a3735ac91a0f3647147acf649dda804e28
7 changes: 5 additions & 2 deletions explanations/contract.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
This is a function you would include in your smart contract to create collections for accounts without them.
The createEmptyCollection function is part of the ExampleNFT contract and is used to create a new, empty collection for an account. A collection is a resource that allows users to store and manage their non-fungible tokens (NFTs) by bundling them together.

When invoked, the createEmptyCollection function creates a new instance of the Collection resource. This resource is initialized with an empty set of NFTs. The collection resource adheres to the CollectionInterface, which defines methods for depositing NFTs, withdrawing NFTs by their ID, and viewing the list of owned NFT IDs.

Once the collection is created, it is returned as a resource, which can then be stored in the account's private storage using a transaction. This enables the account to manage NFTs directly. Additionally, the function creates a public capability for the collection, which grants others access to the functionality provided by the collection (e.g., depositing and withdrawing NFTs) through the CollectionPublic interface.

A collection resource is returned which you then save into your account with a transaction.
4 changes: 2 additions & 2 deletions explanations/transaction.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
This transaction is showing us how we would create a new empty collection and save that resource into a users account.
This transaction demonstrates how to create a new empty collection for an account and save it as a resource in the user's storage. Initially, the transaction checks if the account already has a collection. If one doesn't exist, it proceeds to create a new ExampleNFT.Collection and stores it in the account’s storage under a specified path.

We then create a public capability to this resource by linking the interface that contains functions the public can use on this resource for this account.
After saving the collection, the transaction creates a public capability for the collection. This capability allows others to interact with the collection by providing access to specific functions defined in the NonFungibleToken.CollectionPublic interface, such as viewing or transferring NFTs. The capability is then published to a public path, making it accessible to others who wish to interact with the collection.
Loading

0 comments on commit 32f39e1

Please sign in to comment.