Skip to content

Commit

Permalink
Repo structure
Browse files Browse the repository at this point in the history
  • Loading branch information
lealobanov committed Dec 12, 2024
1 parent f177a0d commit 58673e0
Show file tree
Hide file tree
Showing 7 changed files with 351 additions and 8 deletions.
29 changes: 25 additions & 4 deletions .github/workflows/cadence_lint.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: Run Cadence Lint
name: Run Cadence Contract Compilation, Deployment, Transaction Execution, and Lint
on: push

jobs:
Expand All @@ -9,7 +9,7 @@ jobs:
uses: actions/checkout@v3
with:
submodules: 'true'

- name: Install Flow CLI
run: |
brew update
Expand All @@ -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
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/
187 changes: 187 additions & 0 deletions cadence/contracts/Recipe.cdc
Original file line number Diff line number Diff line change
@@ -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)

}
}
6 changes: 6 additions & 0 deletions cadence/tests/Recipe_test.cdc
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import Test

access(all) fun testExample() {
let array = [1, 2, 3]
Test.expect(array.length, Test.equal(3))
}
36 changes: 36 additions & 0 deletions cadence/transactions/withdraw_token.cdc
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import "ExampleToken"

// This transaction is a template for a transaction that
// could be used by anyone to send tokens to another account
// that owns a Vault
transaction {

// Temporary Vault object that holds the balance that is being transferred
var temporaryVault: @ExampleToken.Vault

prepare(acct: auth(Storage, Capabilities) &Account) {
// Withdraw tokens from your vault by borrowing a reference to it
// and calling the withdraw function with that reference
let vaultRef = acct.capabilities.storage.borrow<&ExampleToken.Vault>(
from: /storage/MainVault
) ?? panic("Could not borrow a reference to the owner's vault")

self.temporaryVault <- vaultRef.withdraw(amount: 10.0)
}

execute {
// Get the recipient's public account object
let recipient = getAccount(0x01)

// Get the recipient's Receiver reference to their Vault
// by borrowing the reference from the public capability
let receiverRef = recipient.capabilities.borrow<&ExampleToken.Vault{ExampleToken.Receiver}>(
/public/MainReceiver
) ?? panic("Could not borrow a reference to the receiver")

// Deposit your tokens to their Vault
receiverRef.deposit(from: <-self.temporaryVault)

log("Transfer succeeded!")
}
}
1 change: 1 addition & 0 deletions emulator-account.pkey
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
0xdc07d83a937644ff362b279501b7f7a3735ac91a0f3647147acf649dda804e28
96 changes: 93 additions & 3 deletions flow.json
Original file line number Diff line number Diff line change
@@ -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"
}
}
},
Expand All @@ -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"
]
}
}
}

0 comments on commit 58673e0

Please sign in to comment.