diff --git a/.github/workflows/cadence_lint.yml b/.github/workflows/cadence_lint.yml new file mode 100644 index 0000000..1100626 --- /dev/null +++ b/.github/workflows/cadence_lint.yml @@ -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 diff --git a/.github/workflows/cadence_tests.yml b/.github/workflows/cadence_tests.yml new file mode 100644 index 0000000..9a51f78 --- /dev/null +++ b/.github/workflows/cadence_tests.yml @@ -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 diff --git a/.gitignore b/.gitignore index 496ee2c..b1d92af 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,3 @@ -.DS_Store \ No newline at end of file +.DS_Store +/imports/ +/.idea/ \ No newline at end of file diff --git a/README.md b/README.md index 69a626f..952a8a7 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,7 @@ This explains the vault resource that can be created in order for you to handle - [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 @@ -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). @@ -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_vault.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 @@ -57,12 +57,6 @@ recipe-name/ - `author`: contributor of the recipe - `playgroundLink`: a link to Flow Playground containing the deployed recipe code - `excerpt`: a brief description of the recipe contents -- `smartContractCode`: path to location of Cadence smart contract code example -- `smartContractExplanation`: path to location of smart contract code explanation -- `transactionCode`: path to location of Cadence transaction code example -- `transactionExplanation`: path to location of transaction code explanation -- `testsPath`: path to location of Cadence test cases code example -- `testsExplanationPath`: path to location of test cases code explanation - `filters`: the filters object is used to perform filtering on recipes in the cookbook - `difficulty`: the difficulty filter supports one of ['beginner', 'intermediate', 'advanced'] @@ -71,17 +65,7 @@ recipe-name/ // Pass the repo name const recipe = "sample-recipe-name"; -//Generate paths of each code file to render -const contractPath = `${recipe}/cadence/contract.cdc`; -const transactionPath = `${recipe}/cadence/transaction.cdc`; -const testsPath = `${recipe}/cadence/tests.cdc`; - -//Generate paths of each explanation file to render -const smartContractExplanationPath = `${recipe}/explanations/contract.txt`; -const transactionExplanationPath = `${recipe}/explanations/transaction.txt`; -const testsExplanationPath = `${recipe}/explanations/tests.txt`; - -export const sampleRecipe= { +export const sampleRecipe = { slug: recipe, title: "", featuredText: "", @@ -89,12 +73,45 @@ export const sampleRecipe= { author: "", playgroundLink: "", excerpt: "", - smartContractCode: contractPath, - smartContractExplanation: smartContractExplanationPath, - transactionCode: transactionPath, - 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 diff --git a/cadence/contract.cdc b/cadence/contract.cdc deleted file mode 100644 index eaa581c..0000000 --- a/cadence/contract.cdc +++ /dev/null @@ -1,39 +0,0 @@ -//A vault resource to be stored in an account that tracks your balance of tokens -pub resource Vault: Provider, Receiver, Balance { - - // keeps track of the total balance of the account's tokens - pub var balance: UFix64 - - // initialize the balance at resource creation time - init(balance: UFix64) { - self.balance = balance - } - - // withdraw - // - // Function that takes an integer amount as an argument - // and withdraws that amount from the Vault. - // - // It creates a new temporary Vault that is used to hold - // the money that is being transferred. It returns the newly - // created Vault to the context that called so it can be deposited - // elsewhere. - // - pub fun withdraw(amount: UFix64): @Vault { - self.balance = self.balance - amount - return <-create Vault(balance: amount) - } - - // deposit - // - // Function that takes a Vault object as an argument and adds - // its balance to the balance of the owners Vault. - // - // It is allowed to destroy the sent Vault because the Vault - // was a temporary holder of the tokens. The Vault's balance has - // been consumed and therefore can be destroyed. - pub fun deposit(from: @Vault) { - self.balance = self.balance + from.balance - destroy from - } -} \ No newline at end of file diff --git a/cadence/contracts/Recipe.cdc b/cadence/contracts/Recipe.cdc new file mode 100644 index 0000000..cbee4c6 --- /dev/null +++ b/cadence/contracts/Recipe.cdc @@ -0,0 +1,187 @@ +/// ExampleToken.cdc +/// +/// The ExampleToken contract is a sample implementation of a fungible token on Flow. +/// +/// Fungible tokens behave like everyday currencies -- they can be minted, transferred or +/// traded for digital goods. +/// +/// This is a basic implementation of a Fungible Token and is NOT meant to be used in production +/// See the Flow Fungible Token standard for real examples: https://github.com/onflow/flow-ft + +access(all) contract ExampleToken { + + access(all) entitlement Withdraw + + access(all) let VaultStoragePath: StoragePath + access(all) let VaultPublicPath: PublicPath + + access(all) var totalSupply: UFix64 + + /// Balance + /// + /// The interface that provides a standard field + /// for representing balance + /// + access(all) resource interface Balance { + access(all) var balance: UFix64 + } + + /// Provider + /// + /// The interface that enforces the requirements for withdrawing + /// tokens from the implementing type. + /// + /// It does not enforce requirements on `balance` here, + /// because it leaves open the possibility of creating custom providers + /// that do not necessarily need their own balance. + /// + access(all) resource interface Provider { + + /// withdraw subtracts tokens from the implementing resource + /// and returns a Vault with the removed tokens. + /// + /// The function's access level is `access(Withdraw)` + /// So in order to access it, one would either need the object itself + /// or an entitled reference with `Withdraw`. + /// + /// @param amount the amount of tokens to withdraw from the resource + /// @return The Vault with the withdrawn tokens + /// + access(Withdraw) fun withdraw(amount: UFix64): @Vault { + post { + // `result` refers to the return value + result.balance == amount: + "ExampleToken.Provider.withdraw: Cannot withdraw tokens!" + .concat("The balance of the withdrawn tokens (").concat(result.balance.toString()) + .concat(") is not equal to the amount requested to be withdrawn (") + .concat(amount.toString()).concat(")") + } + } + } + + /// Receiver + /// + /// The interface that enforces the requirements for depositing + /// tokens into the implementing type. + /// + /// We do not include a condition that checks the balance because + /// we want to give users the ability to make custom receivers that + /// can do custom things with the tokens, like split them up and + /// send them to different places. + /// + access(all) resource interface Receiver { + + /// deposit takes a Vault and deposits it into the implementing resource type + /// + /// @param from the Vault that contains the tokens to deposit + /// + access(all) fun deposit(from: @Vault) + } + + /// Vault + /// + /// Each user stores an instance of only the Vault in their storage + /// The functions in the Vault are governed by the pre and post conditions + /// in the interfaces when they are called. + /// The checks happen at runtime whenever a function is called. + /// + /// Resources can only be created in the context of the contract that they + /// are defined in, so there is no way for a malicious user to create Vaults + /// out of thin air. A special Minter resource or constructor function needs to be defined to mint + /// new tokens. + /// + access(all) resource Vault: Balance, Provider, Receiver { + + /// keeps track of the total balance of the account's tokens + access(all) var balance: UFix64 + + /// initialize the balance at resource creation time + init(balance: UFix64) { + self.balance = balance + } + + /// withdraw + /// + /// Function that takes an integer amount as an argument + /// and withdraws that amount from the Vault. + /// + /// It creates a new temporary Vault that is used to hold + /// the money that is being transferred. It returns the newly + /// created Vault to the context that called so it can be deposited + /// elsewhere. + /// + access(Withdraw) fun withdraw(amount: UFix64): @Vault { + pre { + self.balance >= amount: + "ExampleToken.Vault.withdraw: Cannot withdraw tokens! " + .concat("The amount requested to be withdrawn (").concat(amount.toString()) + .concat(") is greater than the balance of the Vault (") + .concat(self.balance.toString()).concat(").") + } + self.balance = self.balance - amount + return <-create Vault(balance: amount) + } + + /// deposit + /// + /// Function that takes a Vault object as an argument and adds + /// its balance to the balance of the owners Vault. + /// + /// It is allowed to destroy the sent Vault because the Vault + /// was a temporary holder of the tokens. The Vault's balance has + /// been consumed and therefore can be destroyed. + access(all) fun deposit(from: @Vault) { + self.balance = self.balance + from.balance + destroy from + } + } + + /// createEmptyVault + /// + access(all) fun createEmptyVault(): @Vault { + return <-create Vault(balance: 0.0) + } + + // VaultMinter + // + // Resource object that an admin can control to mint new tokens + access(all) resource VaultMinter { + + // Function that mints new tokens and deposits into an account's vault + // using their `{Receiver}` reference. + // We say `&{Receiver}` to say that the recipient can be any resource + // as long as it implements the Receiver interface + access(all) fun mintTokens(amount: UFix64, recipient: Capability<&{Receiver}>) { + let recipientRef = recipient.borrow() + ?? panic("ExampleToken.VaultMinter.mintTokens: Could not borrow a receiver reference to " + .concat("the specified recipient's ExampleToken.Vault") + .concat(". Make sure the account has set up its account ") + .concat("with an ExampleToken Vault and valid capability.")) + + ExampleToken.totalSupply = ExampleToken.totalSupply + UFix64(amount) + recipientRef.deposit(from: <-create Vault(balance: amount)) + } + } + + /// The init function for the contract. All fields in the contract must + /// be initialized at deployment. This is just an example of what + /// an implementation could do in the init function. The numbers are arbitrary. + init() { + self.VaultStoragePath = /storage/CadenceFungibleTokenTutorialVault + self.VaultPublicPath = /public/CadenceFungibleTokenTutorialReceiver + + self.totalSupply = 30.0 + + // create the Vault with the initial balance and put it in storage + // account.save saves an object to the specified `to` path + // The path is a literal path that consists of a domain and identifier + // The domain must be `storage`, `private`, or `public` + // the identifier can be any name + let vault <- create Vault(balance: self.totalSupply) + self.account.storage.save(<-vault, to: self.VaultStoragePath) + + // Create a new VaultMinter resource and store it in account storage + self.account.storage.save(<-create VaultMinter(), to: /storage/CadenceFungibleTokenTutorialMinter) + + } +} \ No newline at end of file diff --git a/cadence/tests/Recipe_test.cdc b/cadence/tests/Recipe_test.cdc new file mode 100644 index 0000000..46606d6 --- /dev/null +++ b/cadence/tests/Recipe_test.cdc @@ -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: "ExampleToken", + path: "../contracts/Recipe.cdc", + arguments: [], + ) + + Test.expect(err, Test.beNil()) +} \ No newline at end of file diff --git a/cadence/transaction.cdc b/cadence/transaction.cdc deleted file mode 100644 index a07cc8f..0000000 --- a/cadence/transaction.cdc +++ /dev/null @@ -1,30 +0,0 @@ -// Setup Account - -import ExampleToken from 0x01 - -// This transaction configures an account to store and receive tokens defined by -// the ExampleToken contract. -transaction { - prepare(acct: AuthAccount) { - // Create a new empty Vault object - let vaultA <- ExampleToken.createEmptyVault() - - // Store the vault in the account storage - acct.save<@ExampleToken.Vault>(<-vaultA, to: /storage/MainVault) - - log("Empty Vault stored") - - // Create a public Receiver capability to the Vault - let ReceiverRef = acct.link<&ExampleToken.Vault{ExampleToken.Receiver, ExampleToken.Balance}>(/public/MainReceiver, target: /storage/MainVault) - - log("References created") - } - - post { - // Check that the capabilities were created correctly - getAccount(0x02).getCapability<&ExampleToken.Vault{ExampleToken.Receiver}>(/public/MainReceiver) - .check(): - "Vault Receiver Reference was not created correctly" - } -} - diff --git a/cadence/transactions/create_vault.cdc b/cadence/transactions/create_vault.cdc new file mode 100644 index 0000000..7293015 --- /dev/null +++ b/cadence/transactions/create_vault.cdc @@ -0,0 +1,33 @@ +import "ExampleToken" + +// This transaction configures an account to store and receive tokens defined by +// the ExampleToken contract. +transaction { + prepare(acct: auth(Storage, Capabilities) &Account) { + // Create a new empty Vault object + let vaultA <- ExampleToken.createEmptyVault() + + // Store the vault in the account storage + acct.storage.save<@ExampleToken.Vault>(<-vaultA, to: /storage/MainVault) + + log("Empty Vault stored") + + // Create a public Receiver capability to the Vault + let receiverCap = acct.capabilities.storage.issue<&ExampleToken.Vault>( + /storage/MainVault + ) + acct.capabilities.publish(receiverCap, at: /public/MainReceiver) + + log("References created") + + // Verify the capability by borrowing the reference + let borrowedVault = acct.capabilities + .borrow<&ExampleToken.Vault>(/public/MainReceiver) + + if borrowedVault != nil { + log("Vault Receiver Reference successfully created and borrowed.") + } else { + log("Vault Receiver Reference creation or borrowing failed.") + } + } +} diff --git a/emulator-account.pkey b/emulator-account.pkey new file mode 100644 index 0000000..75611bd --- /dev/null +++ b/emulator-account.pkey @@ -0,0 +1 @@ +0xdc07d83a937644ff362b279501b7f7a3735ac91a0f3647147acf649dda804e28 \ No newline at end of file diff --git a/explanations/contract.txt b/explanations/contract.txt index 2a7aa2c..e470fbd 100644 --- a/explanations/contract.txt +++ b/explanations/contract.txt @@ -1,5 +1,3 @@ -This is a vault resource in a smart contract that can be stored in a users account. +This smart contract defines a vault resource designed to be stored within a user's account. The vault serves as a secure container for managing a user's token balance, tracking the total amount of tokens held while providing essential functionality for withdrawing and depositing tokens. -It keeps track of the total balance of tokens as well as the functions to withdraw and deposit tokens. - -Interfaces such as Provider, Receiver, and Balance are included that allow users to send you money when executing a transaction. +The vault incorporates key interfaces such as Provider, Receiver, and Balance, which enable token transactions. These interfaces allow users to send and receive tokens efficiently during transactions, ensuring compatibility and interoperability within the broader ecosystem. By integrating these interfaces, the vault resource facilitates streamlined token management and enables robust interactions between accounts in the smart contract. \ No newline at end of file diff --git a/explanations/transaction.txt b/explanations/transaction.txt index 29a9712..603a323 100644 --- a/explanations/transaction.txt +++ b/explanations/transaction.txt @@ -1,5 +1,5 @@ -Here we are creating a new Vault resource into an account. +This transaction creates a new Vault resource and stores it securely within the user's private storage. The Vault serves as a container for managing tokens within the account. -When that is created and stored into their private storage, we then create public capabilities that others can use to check balance, or deposit tokens. +After storing the Vault, the transaction establishes public capabilities linked to it. These capabilities allow others to interact with the Vault, enabling actions such as checking its balance or depositing tokens. This setup ensures the Vault is both accessible for approved interactions and protected within the account's private storage. -Once we do that, we check to make sure that the capabilities were indeed created and then we are done with the transaction. +Finally, the transaction verifies that the public capabilities were successfully created, ensuring the Vault is properly set up for use. \ No newline at end of file diff --git a/flow.json b/flow.json new file mode 100644 index 0000000..a9ca14d --- /dev/null +++ b/flow.json @@ -0,0 +1,107 @@ +{ + "contracts": { + "ExampleToken": { + "source": "./cadence/contracts/Recipe.cdc", + "aliases": { + "emulator": "f8d6e0586b0a20c7", + "testing": "0000000000000007" + } + } + }, + "dependencies": { + "Burner": { + "source": "mainnet://f233dcee88fe0abe.Burner", + "hash": "71af18e227984cd434a3ad00bb2f3618b76482842bae920ee55662c37c8bf331", + "aliases": { + "emulator": "f8d6e0586b0a20c7", + "mainnet": "f233dcee88fe0abe", + "testnet": "9a0766d93b6608b7" + } + }, + "FlowToken": { + "source": "mainnet://1654653399040a61.FlowToken", + "hash": "cefb25fd19d9fc80ce02896267eb6157a6b0df7b1935caa8641421fe34c0e67a", + "aliases": { + "emulator": "0ae53cb6e3f42a79", + "mainnet": "1654653399040a61", + "testnet": "7e60df042a9c0868" + } + }, + "FungibleToken": { + "source": "mainnet://f233dcee88fe0abe.FungibleToken", + "hash": "050328d01c6cde307fbe14960632666848d9b7ea4fef03ca8c0bbfb0f2884068", + "aliases": { + "emulator": "ee82856bf20e2aa6", + "mainnet": "f233dcee88fe0abe", + "testnet": "9a0766d93b6608b7" + } + }, + "FungibleTokenMetadataViews": { + "source": "mainnet://f233dcee88fe0abe.FungibleTokenMetadataViews", + "hash": "dff704a6e3da83997ed48bcd244aaa3eac0733156759a37c76a58ab08863016a", + "aliases": { + "emulator": "ee82856bf20e2aa6", + "mainnet": "f233dcee88fe0abe", + "testnet": "9a0766d93b6608b7" + } + }, + "FungibleTokenSwitchboard": { + "source": "mainnet://f233dcee88fe0abe.FungibleTokenSwitchboard", + "hash": "10f94fe8803bd1c2878f2323bf26c311fb4fb2beadba9f431efdb1c7fa46c695", + "aliases": { + "emulator": "ee82856bf20e2aa6", + "mainnet": "f233dcee88fe0abe", + "testnet": "9a0766d93b6608b7" + } + }, + "MetadataViews": { + "source": "mainnet://1d7e57aa55817448.MetadataViews", + "hash": "10a239cc26e825077de6c8b424409ae173e78e8391df62750b6ba19ffd048f51", + "aliases": { + "emulator": "f8d6e0586b0a20c7", + "mainnet": "1d7e57aa55817448", + "testnet": "631e88ae7f1d7c20" + } + }, + "NonFungibleToken": { + "source": "mainnet://1d7e57aa55817448.NonFungibleToken", + "hash": "b63f10e00d1a814492822652dac7c0574428a200e4c26cb3c832c4829e2778f0", + "aliases": { + "emulator": "f8d6e0586b0a20c7", + "mainnet": "1d7e57aa55817448", + "testnet": "631e88ae7f1d7c20" + } + }, + "ViewResolver": { + "source": "mainnet://1d7e57aa55817448.ViewResolver", + "hash": "374a1994046bac9f6228b4843cb32393ef40554df9bd9907a702d098a2987bde", + "aliases": { + "emulator": "f8d6e0586b0a20c7", + "mainnet": "1d7e57aa55817448", + "testnet": "631e88ae7f1d7c20" + } + } + }, + "networks": { + "emulator": "127.0.0.1:3569", + "mainnet": "access.mainnet.nodes.onflow.org:9000", + "testing": "127.0.0.1:3569", + "testnet": "access.devnet.nodes.onflow.org:9000" + }, + "accounts": { + "emulator-account": { + "address": "f8d6e0586b0a20c7", + "key": { + "type": "file", + "location": "emulator-account.pkey" + } + } + }, + "deployments": { + "emulator": { + "emulator-account": [ + "ExampleToken" + ] + } + } +} \ No newline at end of file diff --git a/index.js b/index.js index 23dbe1d..f7dc085 100644 --- a/index.js +++ b/index.js @@ -1,14 +1,6 @@ // Pass the repo name const recipe = "token-vault"; -//Generate paths of each code file to render -const contractPath = `${recipe}/cadence/contract.cdc`; -const transactionPath = `${recipe}/cadence/transaction.cdc`; - -//Generate paths of each explanation file to render -const smartContractExplanationPath = `${recipe}/explanations/contract.txt`; -const transactionExplanationPath = `${recipe}/explanations/transaction.txt`; - export const tokenVault = { slug: recipe, title: "Token Vault", @@ -18,11 +10,7 @@ export const tokenVault = { "https://play.onflow.org/8b168390-9712-4578-a437-d15125cf18d9", excerpt: "This explains the vault resource that can be created in order for you to handle fungible tokens.", - smartContractCode: contractPath, - smartContractExplanation: smartContractExplanationPath, - transactionCode: transactionPath, - transactionExplanation: transactionExplanationPath, filters: { - difficulty: "beginner" - } + difficulty: "beginner", + }, };