From fd53e4f9aa145c4bc0974f22ee0df644a77be839 Mon Sep 17 00:00:00 2001 From: Lea Lobanov Date: Mon, 18 Nov 2024 22:07:34 +0100 Subject: [PATCH 01/10] C1 migration --- cadence/contract.cdc | 12 +++++++----- cadence/transaction.cdc | 22 ++++++++++------------ index.js | 7 +++---- 3 files changed, 20 insertions(+), 21 deletions(-) diff --git a/cadence/contract.cdc b/cadence/contract.cdc index ca313c9..aa91d0a 100644 --- a/cadence/contract.cdc +++ b/cadence/contract.cdc @@ -1,17 +1,19 @@ // VaultMinter // // Resource object that an admin can control to mint new tokens -pub resource VaultMinter { +access(all) +resource VaultMinter { // Function that mints new tokens and deposits into an account's vault - // using their 'Receiver' reference. - // We say '&AnyResource{Receiver}' to say that the recipient can be any resource + // using their 'Receiver' capability. + // We say '&AnyResource{Receiver}' to specify that the recipient can be any resource // as long as it implements the Receiver interface - pub fun mintTokens(amount: UFix64, recipient: Capability<&AnyResource{Receiver}>) { + access(all) + fun mintTokens(amount: UFix64, recipient: Capability<&AnyResource{Receiver}>) { let recipientRef = recipient.borrow() ?? panic("Could not borrow a receiver reference to the vault") ExampleToken.totalSupply = ExampleToken.totalSupply + UFix64(amount) recipientRef.deposit(from: <-create Vault(balance: amount)) } -} \ No newline at end of file +} diff --git a/cadence/transaction.cdc b/cadence/transaction.cdc index f726585..d324c02 100644 --- a/cadence/transaction.cdc +++ b/cadence/transaction.cdc @@ -1,30 +1,29 @@ -// Mint Tokens - import ExampleToken from 0x01 -// This transaction mints tokens and deposits them into account 2's vault +// This transaction mints tokens and deposits them into account 0x02's vault transaction { // Local variable for storing the reference to the minter resource - let mintingRef: &ExampleToken.VaultMinter + let mintingRef: auth(MintEntitlement) &ExampleToken.VaultMinter // Local variable for storing the reference to the Vault of // the account that will receive the newly minted tokens var receiver: Capability<&ExampleToken.Vault{ExampleToken.Receiver}> - prepare(acct: AuthAccount) { + prepare(acct: auth(Storage, Capabilities) &Account) { // Borrow a reference to the stored, private minter resource - self.mintingRef = acct.borrow<&ExampleToken.VaultMinter>(from: /storage/MainMinter) - ?? panic("Could not borrow a reference to the minter") + self.mintingRef = acct.capabilities.storage.borrow<&ExampleToken.VaultMinter>( + from: /storage/MainMinter + ) ?? panic("Could not borrow a reference to the minter") // Get the public account object for account 0x02 let recipient = getAccount(0x02) // Get their public receiver capability - self.receiver = recipient.getCapability<&ExampleToken.Vault{ExampleToken.Receiver}> -(/public/MainReceiver) - - } + self.receiver = recipient.capabilities.borrow<&ExampleToken.Vault{ExampleToken.Receiver}>( + /public/MainReceiver + ) ?? panic("Could not borrow the receiver capability from account 0x02") + } execute { // Mint 30 tokens and deposit them into the recipient's Vault @@ -33,4 +32,3 @@ transaction { log("30 tokens minted and deposited to account 0x02") } } - diff --git a/index.js b/index.js index e4544fc..87c5a83 100644 --- a/index.js +++ b/index.js @@ -16,13 +16,12 @@ export const vaultMinter = { author: "Flow Blockchain", playgroundLink: "https://play.onflow.org/ef2fe054-148b-4c75-94f1-95bd33b6ce00?type=tx&id=899a81c3-a141-4021-a2b6-0e78ee8a105a", - excerpt: - "This resource is used by the admin to mint more tokens.", + excerpt: "This resource is used by the admin to mint more tokens.", smartContractCode: contractPath, smartContractExplanation: smartContractExplanationPath, transactionCode: transactionPath, transactionExplanation: transactionExplanationPath, filters: { - difficulty: "intermediate" - } + difficulty: "intermediate", + }, }; From 9b48f739100589db658975d40c16377a3d432202 Mon Sep 17 00:00:00 2001 From: Lea Lobanov Date: Fri, 29 Nov 2024 14:05:04 +0000 Subject: [PATCH 02/10] CI GH actions --- .github/workflows/cadence_lint.yml | 30 +++++++++++++++++++++++++ .github/workflows/cadence_tests.yml | 34 +++++++++++++++++++++++++++++ 2 files changed, 64 insertions(+) create mode 100644 .github/workflows/cadence_lint.yml create mode 100644 .github/workflows/cadence_tests.yml diff --git a/.github/workflows/cadence_lint.yml b/.github/workflows/cadence_lint.yml new file mode 100644 index 0000000..58565d0 --- /dev/null +++ b/.github/workflows/cadence_lint.yml @@ -0,0 +1,30 @@ +name: Run Cadence 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 + + - name: Run Cadence Lint + run: | + echo "Running Cadence linter on all .cdc files in the current repository" + flow cadence lint **/*.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 From 5a68a0a31da3a7e101438e45d471a9fc822f3549 Mon Sep 17 00:00:00 2001 From: Lea Lobanov Date: Fri, 29 Nov 2024 14:07:57 +0000 Subject: [PATCH 03/10] Add flow config --- flow.json | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 flow.json diff --git a/flow.json b/flow.json new file mode 100644 index 0000000..e81ec35 --- /dev/null +++ b/flow.json @@ -0,0 +1,16 @@ +{ + "contracts": { + "Counter": { + "source": "cadence/contracts/Counter.cdc", + "aliases": { + "testing": "0000000000000007" + } + } + }, + "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" + } +} \ No newline at end of file From a5d50a099ee7ca3ca9d0b471daf38951c9551c1b Mon Sep 17 00:00:00 2001 From: Lea Lobanov Date: Fri, 13 Dec 2024 02:35:50 +0400 Subject: [PATCH 04/10] Repo structure --- .github/workflows/cadence_lint.yml | 29 +++- cadence/contract.cdc | 20 +-- cadence/contracts/Recipe.cdc | 187 ++++++++++++++++++++++ cadence/tests/Recipe_test.cdc | 6 + cadence/transaction.cdc | 35 +--- cadence/transactions/deposit_to_vault.cdc | 34 ++++ emulator-account.pkey | 1 + flow.json | 96 ++++++++++- 8 files changed, 348 insertions(+), 60 deletions(-) mode change 100644 => 120000 cadence/contract.cdc create mode 100644 cadence/contracts/Recipe.cdc create mode 100644 cadence/tests/Recipe_test.cdc mode change 100644 => 120000 cadence/transaction.cdc create mode 100644 cadence/transactions/deposit_to_vault.cdc create mode 100644 emulator-account.pkey diff --git a/.github/workflows/cadence_lint.yml b/.github/workflows/cadence_lint.yml index 58565d0..1100626 100644 --- a/.github/workflows/cadence_lint.yml +++ b/.github/workflows/cadence_lint.yml @@ -1,4 +1,4 @@ -name: Run Cadence Lint +name: Run Cadence Contract Compilation, Deployment, Transaction Execution, and Lint on: push jobs: @@ -9,7 +9,7 @@ jobs: uses: actions/checkout@v3 with: submodules: 'true' - + - name: Install Flow CLI run: | brew update @@ -23,8 +23,29 @@ jobs: 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 all .cdc files in the current repository" - flow cadence lint **/*.cdc + echo "Running Cadence linter on .cdc files in the current repository" + flow cadence lint ./cadence/**/*.cdc diff --git a/cadence/contract.cdc b/cadence/contract.cdc deleted file mode 100644 index aa91d0a..0000000 --- a/cadence/contract.cdc +++ /dev/null @@ -1,19 +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' capability. - // We say '&AnyResource{Receiver}' to specify that the recipient can be any resource - // as long as it implements the Receiver interface - access(all) - fun mintTokens(amount: UFix64, recipient: Capability<&AnyResource{Receiver}>) { - let recipientRef = recipient.borrow() - ?? panic("Could not borrow a receiver reference to the vault") - - ExampleToken.totalSupply = ExampleToken.totalSupply + UFix64(amount) - recipientRef.deposit(from: <-create Vault(balance: amount)) - } -} diff --git a/cadence/contract.cdc b/cadence/contract.cdc new file mode 120000 index 0000000..b64184f --- /dev/null +++ b/cadence/contract.cdc @@ -0,0 +1 @@ +./cadence/contracts/Recipe.cdc \ 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..986e8fe --- /dev/null +++ b/cadence/tests/Recipe_test.cdc @@ -0,0 +1,6 @@ +import Test + +access(all) fun testExample() { + let array = [1, 2, 3] + Test.expect(array.length, Test.equal(3)) +} diff --git a/cadence/transaction.cdc b/cadence/transaction.cdc deleted file mode 100644 index d324c02..0000000 --- a/cadence/transaction.cdc +++ /dev/null @@ -1,34 +0,0 @@ -import ExampleToken from 0x01 - -// This transaction mints tokens and deposits them into account 0x02's vault -transaction { - - // Local variable for storing the reference to the minter resource - let mintingRef: auth(MintEntitlement) &ExampleToken.VaultMinter - - // Local variable for storing the reference to the Vault of - // the account that will receive the newly minted tokens - var receiver: Capability<&ExampleToken.Vault{ExampleToken.Receiver}> - - prepare(acct: auth(Storage, Capabilities) &Account) { - // Borrow a reference to the stored, private minter resource - self.mintingRef = acct.capabilities.storage.borrow<&ExampleToken.VaultMinter>( - from: /storage/MainMinter - ) ?? panic("Could not borrow a reference to the minter") - - // Get the public account object for account 0x02 - let recipient = getAccount(0x02) - - // Get their public receiver capability - self.receiver = recipient.capabilities.borrow<&ExampleToken.Vault{ExampleToken.Receiver}>( - /public/MainReceiver - ) ?? panic("Could not borrow the receiver capability from account 0x02") - } - - execute { - // Mint 30 tokens and deposit them into the recipient's Vault - self.mintingRef.mintTokens(amount: 30.0, recipient: self.receiver) - - log("30 tokens minted and deposited to account 0x02") - } -} diff --git a/cadence/transaction.cdc b/cadence/transaction.cdc new file mode 120000 index 0000000..e782c58 --- /dev/null +++ b/cadence/transaction.cdc @@ -0,0 +1 @@ +./cadence/transactions/deposit_to_vault.cdc \ No newline at end of file diff --git a/cadence/transactions/deposit_to_vault.cdc b/cadence/transactions/deposit_to_vault.cdc new file mode 100644 index 0000000..d324c02 --- /dev/null +++ b/cadence/transactions/deposit_to_vault.cdc @@ -0,0 +1,34 @@ +import ExampleToken from 0x01 + +// This transaction mints tokens and deposits them into account 0x02's vault +transaction { + + // Local variable for storing the reference to the minter resource + let mintingRef: auth(MintEntitlement) &ExampleToken.VaultMinter + + // Local variable for storing the reference to the Vault of + // the account that will receive the newly minted tokens + var receiver: Capability<&ExampleToken.Vault{ExampleToken.Receiver}> + + prepare(acct: auth(Storage, Capabilities) &Account) { + // Borrow a reference to the stored, private minter resource + self.mintingRef = acct.capabilities.storage.borrow<&ExampleToken.VaultMinter>( + from: /storage/MainMinter + ) ?? panic("Could not borrow a reference to the minter") + + // Get the public account object for account 0x02 + let recipient = getAccount(0x02) + + // Get their public receiver capability + self.receiver = recipient.capabilities.borrow<&ExampleToken.Vault{ExampleToken.Receiver}>( + /public/MainReceiver + ) ?? panic("Could not borrow the receiver capability from account 0x02") + } + + execute { + // Mint 30 tokens and deposit them into the recipient's Vault + self.mintingRef.mintTokens(amount: 30.0, recipient: self.receiver) + + log("30 tokens minted and deposited to account 0x02") + } +} 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/flow.json b/flow.json index e81ec35..3d1d7cb 100644 --- a/flow.json +++ b/flow.json @@ -1,9 +1,83 @@ { "contracts": { - "Counter": { - "source": "cadence/contracts/Counter.cdc", + "ExampleToken": { + "source": "./cadence/contracts/Recipe.cdc", "aliases": { - "testing": "0000000000000007" + "emulator": "f8d6e0586b0a20c7" + } + } + }, + "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" } } }, @@ -12,5 +86,21 @@ "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 From 85b9e888472e6e305ffdc769009adaa691c6d1d0 Mon Sep 17 00:00:00 2001 From: Lea Lobanov Date: Fri, 13 Dec 2024 02:37:43 +0400 Subject: [PATCH 05/10] Repo structure --- README.md | 61 ++++++++++++++++++----- cadence/transactions/deposit_to_vault.cdc | 2 +- 2 files changed, 50 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 965a7c4..64c1cdb 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,7 @@ This resource is used by the admin to mint more tokens. - [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/deposit_to_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 @@ -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 diff --git a/cadence/transactions/deposit_to_vault.cdc b/cadence/transactions/deposit_to_vault.cdc index d324c02..4633c95 100644 --- a/cadence/transactions/deposit_to_vault.cdc +++ b/cadence/transactions/deposit_to_vault.cdc @@ -1,4 +1,4 @@ -import ExampleToken from 0x01 +import "ExampleToken" // This transaction mints tokens and deposits them into account 0x02's vault transaction { From 4f77b75b43b4636a2915cbd95c5a632d51b7df3c Mon Sep 17 00:00:00 2001 From: Lea Lobanov Date: Fri, 13 Dec 2024 02:47:46 +0400 Subject: [PATCH 06/10] Debug tx --- cadence/transactions/deposit_to_vault.cdc | 32 +++++++++++------------ 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/cadence/transactions/deposit_to_vault.cdc b/cadence/transactions/deposit_to_vault.cdc index 4633c95..9bc2b2d 100644 --- a/cadence/transactions/deposit_to_vault.cdc +++ b/cadence/transactions/deposit_to_vault.cdc @@ -1,34 +1,32 @@ import "ExampleToken" -// This transaction mints tokens and deposits them into account 0x02's vault +// This transaction mints tokens and deposits them into the caller's vault transaction { // Local variable for storing the reference to the minter resource - let mintingRef: auth(MintEntitlement) &ExampleToken.VaultMinter + let mintingRef: &ExampleToken.VaultMinter - // Local variable for storing the reference to the Vault of - // the account that will receive the newly minted tokens - var receiver: Capability<&ExampleToken.Vault{ExampleToken.Receiver}> + // Local variable for storing the receiver capability of the caller + var receiver: Capability<&{ExampleToken.Receiver}> prepare(acct: auth(Storage, Capabilities) &Account) { // Borrow a reference to the stored, private minter resource - self.mintingRef = acct.capabilities.storage.borrow<&ExampleToken.VaultMinter>( - from: /storage/MainMinter + let minter = acct.storage.borrow<&ExampleToken.VaultMinter>( + from: /storage/CadenceFungibleTokenTutorialMinter ) ?? panic("Could not borrow a reference to the minter") + self.mintingRef = minter - // Get the public account object for account 0x02 - let recipient = getAccount(0x02) - - // Get their public receiver capability - self.receiver = recipient.capabilities.borrow<&ExampleToken.Vault{ExampleToken.Receiver}>( - /public/MainReceiver - ) ?? panic("Could not borrow the receiver capability from account 0x02") + // Issue a Receiver capability for the caller's Vault + let receiverCap = acct.capabilities.storage.issue<&{ExampleToken.Receiver}>( + /storage/CadenceFungibleTokenTutorialVault + ) + self.receiver = receiverCap } execute { - // Mint 30 tokens and deposit them into the recipient's Vault + // Mint 30 tokens and deposit them into the caller's Vault self.mintingRef.mintTokens(amount: 30.0, recipient: self.receiver) - log("30 tokens minted and deposited to account 0x02") + log("30 tokens minted and deposited to the caller's account") } -} +} \ No newline at end of file From 89dc167f4b98e43f27732da954e61c60662eb908 Mon Sep 17 00:00:00 2001 From: Lea Lobanov Date: Fri, 13 Dec 2024 02:49:37 +0400 Subject: [PATCH 07/10] Debug tx --- explanations/contract.txt | 7 +++---- explanations/transaction.txt | 4 ++-- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/explanations/contract.txt b/explanations/contract.txt index ef5e376..faa827a 100644 --- a/explanations/contract.txt +++ b/explanations/contract.txt @@ -1,7 +1,6 @@ -This resource is created so that more tokens may be minted. Ideally not many people should have access to this, only admins. -The VaultMinter resource takes in an amount as well as a capability that implements the Receiver interface as its arguments. +The VaultMinter resource is designed to allow the controlled minting of new tokens. Access to this resource should be strictly limited, ideally to admins or other highly trusted entities, to maintain the integrity of the token supply. -It checks to make sure the capability exists to receive, and once it does that it adds the amount in the parameters to the total minted supply. +The VaultMinter provides a mintTokens function that takes two arguments: the amount of tokens to mint and a capability that implements the Receiver interface. Before proceeding, it validates that the provided capability can accept deposits. Once the capability is verified, the specified amount of tokens is added to the total minted supply, ensuring accurate tracking of the token supply. -Afterwards that newly created balance is deposited into the receivers account. +Finally, the newly minted tokens are encapsulated in a Vault resource and deposited directly into the account associated with the provided Receiver capability. \ No newline at end of file diff --git a/explanations/transaction.txt b/explanations/transaction.txt index 557be39..4da13c6 100644 --- a/explanations/transaction.txt +++ b/explanations/transaction.txt @@ -1,3 +1,3 @@ -When doing the transaction, you first need to check to see if there is a VaultMinter that can be referenced by the signer of the transaction. +To execute this transaction, the first step is to verify that the signer of the transaction has access to a VaultMinter resource. This resource is required for minting new tokens. If the VaultMinter is available, you then retrieve the account that will receive the minted tokens. This account must have a valid Receiver capability to accept the tokens. -If so, then you get an account that has the capability to receive tokens and once you execute the transaction you include the amount of tokens to be minted, as well as the receivers capability in the arguments. +During the execution phase of the transaction, you specify the amount of tokens to be minted and provide the recipient's Receiver capability as arguments. The VaultMinter will mint the specified tokens, validate the recipient's capability, and securely deposit the newly minted tokens into the recipient's account. \ No newline at end of file From eb6fe2a707f7b9573259ee43e2254ff685224c45 Mon Sep 17 00:00:00 2001 From: Lea Lobanov Date: Fri, 13 Dec 2024 03:12:13 +0400 Subject: [PATCH 08/10] Debug tx --- .gitignore | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) 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 From 82d6fba8d3f1baa22d01f80cf82e9e62b5bec232 Mon Sep 17 00:00:00 2001 From: Lea Lobanov Date: Mon, 16 Dec 2024 02:59:53 +0400 Subject: [PATCH 09/10] Update tests --- cadence/tests/Recipe_test.cdc | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/cadence/tests/Recipe_test.cdc b/cadence/tests/Recipe_test.cdc index 986e8fe..46606d6 100644 --- a/cadence/tests/Recipe_test.cdc +++ b/cadence/tests/Recipe_test.cdc @@ -4,3 +4,14 @@ 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 From 162dd04c7bdf3888cb31a40c5950727b92d9e15d Mon Sep 17 00:00:00 2001 From: Jerome P Date: Mon, 16 Dec 2024 11:10:42 -0800 Subject: [PATCH 10/10] Fix to make test load Receipe contract when run --- flow.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/flow.json b/flow.json index 3d1d7cb..a9ca14d 100644 --- a/flow.json +++ b/flow.json @@ -3,7 +3,8 @@ "ExampleToken": { "source": "./cadence/contracts/Recipe.cdc", "aliases": { - "emulator": "f8d6e0586b0a20c7" + "emulator": "f8d6e0586b0a20c7", + "testing": "0000000000000007" } } },