├── .gitignore ├── README.md ├── js ├── README.md ├── package-lock.json ├── package.json ├── scripts │ ├── signing-api │ │ └── index.js │ └── simple │ │ └── index.js └── util │ └── util.js └── pact ├── dapp-contracts ├── README.md ├── coin-contract │ ├── coin.pact │ ├── coin.repl │ └── fungible-v2.pact ├── covid │ ├── README.md │ ├── covid.pact │ ├── covid.repl │ └── dashboard.png ├── crowdfund │ ├── README.md │ ├── crowdfund-pacts.pact │ └── crowdfund.repl ├── faucet │ ├── README.md │ ├── testnet-faucet.pact │ ├── testnet-faucet.png │ └── testnet-faucet.repl ├── hybrid-dapp │ ├── README.md │ ├── hybrid-demo.png │ ├── hybrid-exchange.pact │ └── hybrid-exchange.repl ├── loans │ ├── README.md │ ├── loans.pact │ └── loans.repl ├── memory-wall │ ├── README.md │ ├── hello-world.pact │ ├── hello-world.repl │ └── hello.png ├── namespaces │ ├── ns.pact │ └── ns.repl ├── pacty-parrot │ ├── README.md │ ├── pacty-parrots.pact │ ├── pacty-parrots.png │ └── pacty-parrots.repl ├── stablecoin │ ├── README.md │ ├── my-token.pact │ └── my-token.repl └── todomvc │ ├── README.md │ ├── todomvc.png │ ├── todos.pact │ └── todos.repl ├── gas-station ├── README.md ├── gas-guard │ ├── create-gas-station.pact │ └── guards │ │ ├── guards.pact │ │ ├── guards.repl │ │ ├── guards1.pact │ │ └── guards1.repl └── gas-payer │ ├── covid-gas-station.pact │ ├── gas-payer-v1-reference.pact │ ├── gas-payer-v1-reference.repl │ └── gas-payer-v1.pact └── utility ├── README.md ├── safe-rotate.pact └── safe-transfer.pact /.gitignore: -------------------------------------------------------------------------------- 1 | */node_modules 2 | */package-lock.json 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Developer Scripts 2 | 3 | This project is to provide developers with useful Smart Contract samples and JS tools to easily interact with Kadena Network. 4 | 5 | ## Smart Contracts 6 | - [Stable Coin](pact/dapp-contracts/stablecoin) 7 | - [Hybrid Dapp](pact/dapp-contracts/hybrid-dapp) 8 | - [Covid Tracking App](pact/dapp-contracts/covid) 9 | - [Coin Faucet](pact/dapp-contracts/faucet) 10 | - [Pacty Parrots](pact/dapp-contracts/pacty-parrot) 11 | - [Manage Loans](pact/dapp-contracts/loans) 12 | - [Memory Wall](pact/dapp-contracts/memory-wall) 13 | - [Todo MVC](pact/dapp-contracts/todomvc) 14 | 15 | ## Gas Station 16 | - [Gas Guard](pact/gas-station/gas-guard) 17 | - [Gas Payer](pact/gas-station/gas-payer) 18 | 19 | ## Utility Scipts 20 | - [Safe Transfer](pact/utility/safe-transfer) 21 | - [Safe Rotate](pact/utility/safe-rotate) 22 | 23 | ## Javascript 24 | - [Simple Command Requests](js) 25 | - [Signing API](js) 26 | 27 | ## Other Resources 28 | - [Pact Smart Contract Language Reference](https://pact-language.readthedocs.io/) 29 | - [Pact Developer Tutorial](https://pactlang.org/) 30 | 31 | Play around with the samples and try deploying your own contracts or gas stations. You can easily interact with Kadena Mainnet or Testnet with our Developer-friendly wallet, [Chainweaver](https://www.kadena.io/chainweaver) 32 | -------------------------------------------------------------------------------- /js/README.md: -------------------------------------------------------------------------------- 1 | # Pact and Javascript 2 | 3 | This folder contains useful scripts to communicate with Pact servers (nodes) in javascript using [pact-lang-api](https://github.com/kadena-io/pact-lang-api) library. 4 | 5 | ## Setup 6 | Install Node >= 8.11.4 7 | 8 | Install All Dependencies. The dependencies include Pact Lang API. 9 | ``` 10 | npm install 11 | ``` 12 | 13 | ## Simple Requests 14 | Run the following command to learn the simplest way to use the basic Pact API endpoints: `/send` `/local` `/poll` `/listen`. 15 | 16 | Learn about the Building Pact Commands and using the Pact Rest API [here](https://pact-language.readthedocs.io/en/latest/pact-reference.html#rest-api) 17 | 18 | ``` 19 | npm run simple 20 | ``` 21 | 22 | ## Signing API 23 | You will need to have [Chainweaver](https://www.kadena.io/chainweaver) installed and open. Learn the coolest way of signing an already built command. 24 | ``` 25 | npm run sign 26 | ``` 27 | -------------------------------------------------------------------------------- /js/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "javascript-sample", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "simple": "node scripts/simple/index.js", 8 | "sign": "node scripts/signing-api/index.js" 9 | }, 10 | "author": "Hee Kyun Yun", 11 | "license": "ISC", 12 | "dependencies": { 13 | "pact-lang-api": "^4.1.2", 14 | "readline": "^1.3.0" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /js/scripts/signing-api/index.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | "use strict"; 3 | 4 | const Pact = require("pact-lang-api"); 5 | const { question, exitMessage } = require("../../util/util") 6 | const fetch = require("node-fetch") 7 | const sampleBuilder = (sender) => { 8 | if (!sender) sender = "noSender" 9 | return { 10 | pactCode: `(format "Hello" [])`, 11 | caps: [Pact.lang.mkCap( 12 | //Role of the Capability 13 | "Sign for Gas Fee", 14 | //Description of the Capability 15 | "Capability to scope the signature for GAS fee", 16 | //Name of the Capability 17 | "coin.GAS", 18 | //Arguments of the Capability 19 | [] 20 | )], 21 | envData: {}, 22 | sender: sender, 23 | chainId: "0", 24 | gasLimit: 600, 25 | nonce: "Developer Script - Signing Api", 26 | ttl: 600 27 | } 28 | }; 29 | 30 | const apiHost = (node, networkId, chainId) => `https://${node}/chainweb/0.0/${networkId}/chain/${chainId}/pact`; 31 | 32 | const main = async () => { 33 | 34 | let sender = await question("Sample Script to use Signing Api to sign a command. Please have your Chainweaver open. If you have an testnet account, type in your account. Else, type in anything.\n"); 35 | const cmd = sampleBuilder(sender); 36 | 37 | console.log("Printing the Command Details...\n"); 38 | Object.keys(cmd).forEach( key => { 39 | console.log(key, ": ", cmd[key]); 40 | }) 41 | 42 | await question("\nSending Request to Chainweaver to sign command. Enter to Continue\n"); 43 | const signedCmd = await Pact.wallet.sign(cmd); 44 | console.log(signedCmd) 45 | 46 | exitMessage("End of the script") 47 | } 48 | 49 | main(); 50 | -------------------------------------------------------------------------------- /js/scripts/simple/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const Pact = require("pact-lang-api"); 4 | const { question, exitMessage } = require("../../util/util") 5 | 6 | const apiHost = (node, networkId, chainId) => `https://${node}/chainweb/0.0/${networkId}/chain/${chainId}/pact`; 7 | const creationTime = () => Math.round((new Date).getTime()/1000); 8 | 9 | const sampleBuilder = (name) => { 10 | if (!name) name = "Anonymous" 11 | return { 12 | keyPairs: {...Pact.crypto.genKeyPair(), clist: [ 13 | { 14 | name: `free.memory-wall-gas-station.GAS_PAYER`, 15 | args: ["gas-payer", {int: 0}, 1.0] 16 | }]}, 17 | type: "exec", 18 | pactCode: `(free.memory-wall.here ${JSON.stringify(name)})`, 19 | nonce: "Developer Script - simple", 20 | envData: {}, 21 | meta: Pact.lang.mkMeta("mw-free-gas" , "0", 0.00000000001, 350, creationTime(), 600), 22 | networkId: "testnet04" 23 | } 24 | }; 25 | 26 | const main = async () => { 27 | 28 | let name = await question("Sample Script to create Pact command and call them. What's your name?\n"); 29 | const cmd = sampleBuilder(name); 30 | 31 | console.log("Printing the Command Details...\n"); 32 | Object.keys(cmd).forEach( key => { 33 | console.log(key, ": ", cmd[key]); 34 | }) 35 | 36 | let {keyPairs, nonce, pactCode, envData, meta, networkId} = cmd; 37 | await question("\nCreating Local Command. Enter to Continue\n"); 38 | 39 | // Creates a command to send as POST to /api/local 40 | let localCmd = Pact.simple.exec.createLocalCommand(keyPairs, nonce, pactCode, envData, meta, networkId) 41 | console.log(localCmd) 42 | 43 | await question("\nCreating Exec Command. Enter to Continue\n") 44 | 45 | // Creates a command to send as POST to /api/send 46 | let execCmd = Pact.simple.exec.createCommand([], nonce, pactCode, envData, meta, networkId); 47 | console.log(execCmd) 48 | 49 | // Send the command to /local enpoint and retrieve preview result 50 | await question("\nSending request to /local endpoint to preview the result. Enter to Continue.\n") 51 | await Pact.fetch.local(cmd, apiHost("us1.testnet.chainweb.com", "testnet04", "0")) 52 | .then(console.log) 53 | 54 | // Send the command to /send enpoint and retrieve transaction requestKey 55 | await question("\nSending request to /send endpoint with the sample command. Enter to Continue.\n") 56 | let txRes = await Pact.fetch.send(cmd, apiHost("us1.testnet.chainweb.com", "testnet04", "0")) 57 | console.log(txRes) 58 | if (!txRes.requestKeys) exitMessage("Send Request Failed"); 59 | 60 | // Send the requestKey to /listen endpoint and retrieve transaction Result 61 | await question("\nSending request to /listen endpoint to fetch the result. Wait ~30 seconds after sending in transaction. Enter to Continue.\n") 62 | await Pact.fetch.listen({listen: txRes.requestKeys[0]}, apiHost("us1.testnet.chainweb.com", "testnet04", "0")) 63 | .then(console.log) 64 | 65 | // Send the requestKey to /poll endpoint and retrieve transaction Result 66 | await question("\nSending request to /poll endpoint to fetch the result. Wait ~30 seconds after sending in transaction. Enter to Continue.\n") 67 | await Pact.fetch.poll(txRes, apiHost("us1.testnet.chainweb.com", "testnet04", "0")) 68 | .then(console.log) 69 | 70 | exitMessage("End of the script") 71 | } 72 | 73 | main(); 74 | -------------------------------------------------------------------------------- /js/util/util.js: -------------------------------------------------------------------------------- 1 | const readline = require('readline'); 2 | 3 | const rl = readline.createInterface({ 4 | input : process.stdin, 5 | output : process.stdout 6 | }); 7 | 8 | const question = (theQuestion) => { 9 | return new Promise(resolve => rl.question(theQuestion, answ => resolve(answ))) 10 | } 11 | 12 | const exitMessage = (msg) => { 13 | console.log("\nEXITING... ", msg) 14 | process.exit() 15 | } 16 | 17 | module.exports = { 18 | question: question, 19 | exitMessage: exitMessage 20 | } 21 | -------------------------------------------------------------------------------- /pact/dapp-contracts/README.md: -------------------------------------------------------------------------------- 1 | # DApp Contracts 2 | 3 | Smart Contracts for Dapps on Kadena Mainnet and Testnet. 4 | Learn about writing Pact development on Atom SDK [here](https://pactlang.org/beginner/pact-on-atom-sdk/) 5 | 6 | ## [Stable Coin](stablecoin) 7 | - Sample Contract to create a custom token, `my-token`. 8 | 9 | ## [Hybrid Dapp](hybrid-dapp) 10 | - Hybrid Application to transfer tokens between Public and Private blockchains 11 | - Github: https://github.com/fmelp/hybrid-dapp 12 | - Demo: https://hybrid.chainweb.com/ 13 | 14 | ## [Covid Tracking App](covid) 15 | - COVID-19 Test Verification and Result Tracking System 16 | - Github: https://github.com/kadena-io/covid19-platform/ 17 | - Demo: https://covid19-test.chainweb.com/ 18 | 19 | ## [Coin Faucet](faucet) 20 | - Coin Faucet to give out Testnet Coins upon request. 21 | - Demo: https://faucet.testnet.chainweb.com/ 22 | 23 | ## [Pacty Parrots](pacty-parrot) 24 | - Parrots Game Contract that draws rewards based on block height and previous block hash. 25 | - Demo: https://pactyparrots.testnet.chainweb.com/ 26 | 27 | ## [Crowdfunding](crowdfund) 28 | - Decentralized crowdfunding contract that uses Pact's unique multi-step transactions, [pacts](https://pact-language.readthedocs.io/en/latest/pact-reference.html#asynchronous-transaction-automation-with-pacts). 29 | 30 | ## [Manage Loans](loans) 31 | - Loan Managing Contract to create, distribute, and manage loans. 32 | - Tutorial: https://pactlang.org/beginner/project-loans 33 | 34 | ## [Memory Wall](memory-wall) 35 | - Simple "hello world" Contract on Kadena Mainnet Blockchain. 36 | - Demo: https://hello.chainweb.com 37 | 38 | ## [Todo MVC](todomvc) 39 | - Todo MVC Demo on Pact Server 40 | - Github: https://github.com/kadena-io/pact-todomvc 41 | -------------------------------------------------------------------------------- /pact/dapp-contracts/coin-contract/coin.pact: -------------------------------------------------------------------------------- 1 | (module coin GOVERNANCE 2 | 3 | @doc "'coin' represents the Kadena Coin Contract. This contract provides both the \ 4 | \buy/redeem gas support in the form of 'fund-tx', as well as transfer, \ 5 | \credit, debit, coinbase, account creation and query, as well as SPV burn \ 6 | \create. To access the coin contract, you may use its fully-qualified name, \ 7 | \or issue the '(use coin)' command in the body of a module declaration." 8 | 9 | @model 10 | [ (defproperty conserves-mass 11 | (= (column-delta coin-table 'balance) 0.0)) 12 | 13 | (defproperty valid-account (account:string) 14 | (and 15 | (>= (length account) 3) 16 | (<= (length account) 256))) 17 | ] 18 | 19 | (implements fungible-v2) 20 | 21 | ; -------------------------------------------------------------------------- 22 | ; Schemas and Tables 23 | 24 | (defschema coin-schema 25 | @doc "The coin contract token schema" 26 | @model [ (invariant (>= balance 0.0)) ] 27 | 28 | balance:decimal 29 | guard:guard) 30 | 31 | (deftable coin-table:{coin-schema}) 32 | 33 | ; -------------------------------------------------------------------------- 34 | ; Capabilities 35 | 36 | (defcap GOVERNANCE () 37 | (enforce false "Enforce non-upgradeability")) 38 | 39 | (defcap GAS () 40 | "Magic capability to protect gas buy and redeem" 41 | true) 42 | 43 | (defcap COINBASE () 44 | "Magic capability to protect miner reward" 45 | true) 46 | 47 | (defcap GENESIS () 48 | "Magic capability constraining genesis transactions" 49 | true) 50 | 51 | (defcap REMEDIATE () 52 | "Magic capability for remediation transactions" 53 | true) 54 | 55 | (defcap DEBIT (sender:string) 56 | "Capability for managing debiting operations" 57 | (enforce-guard (at 'guard (read coin-table sender))) 58 | (enforce (!= sender "") "valid sender")) 59 | 60 | (defcap CREDIT (receiver:string) 61 | "Capability for managing crediting operations" 62 | (enforce (!= receiver "") "valid receiver")) 63 | 64 | (defcap ROTATE (account:string) 65 | @doc "Autonomously managed capability for guard rotation" 66 | @managed 67 | true) 68 | 69 | (defcap TRANSFER:bool 70 | ( sender:string 71 | receiver:string 72 | amount:decimal 73 | ) 74 | @managed amount TRANSFER-mgr 75 | (enforce (!= sender receiver) "same sender and receiver") 76 | (enforce-unit amount) 77 | (enforce (> amount 0.0) "Positive amount") 78 | (compose-capability (DEBIT sender)) 79 | (compose-capability (CREDIT receiver)) 80 | ) 81 | 82 | (defun TRANSFER-mgr:decimal 83 | ( managed:decimal 84 | requested:decimal 85 | ) 86 | 87 | (let ((newbal (- managed requested))) 88 | (enforce (>= newbal 0.0) 89 | (format "TRANSFER exceeded for balance {}" [managed])) 90 | newbal) 91 | ) 92 | 93 | ; -------------------------------------------------------------------------- 94 | ; Constants 95 | 96 | (defconst COIN_CHARSET CHARSET_LATIN1 97 | "The default coin contract character set") 98 | 99 | (defconst MINIMUM_PRECISION 12 100 | "Minimum allowed precision for coin transactions") 101 | 102 | (defconst MINIMUM_ACCOUNT_LENGTH 3 103 | "Minimum account length admissible for coin accounts") 104 | 105 | (defconst MAXIMUM_ACCOUNT_LENGTH 256 106 | "Maximum account name length admissible for coin accounts") 107 | 108 | ; -------------------------------------------------------------------------- 109 | ; Utilities 110 | 111 | (defun enforce-unit:bool (amount:decimal) 112 | @doc "Enforce minimum precision allowed for coin transactions" 113 | 114 | (enforce 115 | (= (floor amount MINIMUM_PRECISION) 116 | amount) 117 | (format "Amount violates minimum precision: {}" [amount])) 118 | ) 119 | 120 | (defun validate-account (account:string) 121 | @doc "Enforce that an account name conforms to the coin contract \ 122 | \minimum and maximum length requirements, as well as the \ 123 | \latin-1 character set." 124 | 125 | (enforce 126 | (is-charset COIN_CHARSET account) 127 | (format 128 | "Account does not conform to the coin contract charset: {}" 129 | [account])) 130 | 131 | (let ((account-length (length account))) 132 | 133 | (enforce 134 | (>= account-length MINIMUM_ACCOUNT_LENGTH) 135 | (format 136 | "Account name does not conform to the min length requirement: {}" 137 | [account])) 138 | 139 | (enforce 140 | (<= account-length MAXIMUM_ACCOUNT_LENGTH) 141 | (format 142 | "Account name does not conform to the max length requirement: {}" 143 | [account])) 144 | ) 145 | ) 146 | 147 | ; -------------------------------------------------------------------------- 148 | ; Coin Contract 149 | 150 | (defun gas-only () 151 | "Predicate for gas-only user guards." 152 | (require-capability (GAS))) 153 | 154 | (defun gas-guard (guard:guard) 155 | "Predicate for gas + single key user guards" 156 | (enforce-one 157 | "Enforce either the presence of a GAS cap or keyset" 158 | [ (gas-only) 159 | (enforce-guard guard) 160 | ])) 161 | 162 | (defun buy-gas:string (sender:string total:decimal) 163 | @doc "This function describes the main 'gas buy' operation. At this point \ 164 | \MINER has been chosen from the pool, and will be validated. The SENDER \ 165 | \of this transaction has specified a gas limit LIMIT (maximum gas) for \ 166 | \the transaction, and the price is the spot price of gas at that time. \ 167 | \The gas buy will be executed prior to executing SENDER's code." 168 | 169 | @model [ (property (> total 0.0)) 170 | (property (valid-account sender)) 171 | ] 172 | 173 | (validate-account sender) 174 | 175 | (enforce-unit total) 176 | (enforce (> total 0.0) "gas supply must be a positive quantity") 177 | 178 | (require-capability (GAS)) 179 | (with-capability (DEBIT sender) 180 | (debit sender total)) 181 | ) 182 | 183 | (defun redeem-gas:string (miner:string miner-guard:guard sender:string total:decimal) 184 | @doc "This function describes the main 'redeem gas' operation. At this \ 185 | \point, the SENDER's transaction has been executed, and the gas that \ 186 | \was charged has been calculated. MINER will be credited the gas cost, \ 187 | \and SENDER will receive the remainder up to the limit" 188 | 189 | @model [ (property (> total 0.0)) 190 | (property (valid-account sender)) 191 | (property (valid-account miner)) 192 | ] 193 | 194 | (validate-account sender) 195 | (validate-account miner) 196 | (enforce-unit total) 197 | 198 | (require-capability (GAS)) 199 | (let* 200 | ((fee (read-decimal "fee")) 201 | (refund (- total fee))) 202 | 203 | (enforce-unit fee) 204 | (enforce (>= fee 0.0) 205 | "fee must be a non-negative quantity") 206 | 207 | (enforce (>= refund 0.0) 208 | "refund must be a non-negative quantity") 209 | 210 | ; directly update instead of credit 211 | (with-capability (CREDIT sender) 212 | (if (> refund 0.0) 213 | (with-read coin-table sender 214 | { "balance" := balance } 215 | (update coin-table sender 216 | { "balance": (+ balance refund) })) 217 | 218 | "noop")) 219 | 220 | (with-capability (CREDIT miner) 221 | (if (> fee 0.0) 222 | (credit miner miner-guard fee) 223 | "noop")) 224 | ) 225 | 226 | ) 227 | 228 | (defun create-account:string (account:string guard:guard) 229 | @model [ (property (valid-account account)) ] 230 | 231 | (validate-account account) 232 | 233 | (insert coin-table account 234 | { "balance" : 0.0 235 | , "guard" : guard 236 | }) 237 | ) 238 | 239 | (defun get-balance:decimal (account:string) 240 | (with-read coin-table account 241 | { "balance" := balance } 242 | balance 243 | ) 244 | ) 245 | 246 | (defun details:object{fungible-v2.account-details} 247 | ( account:string ) 248 | (with-read coin-table account 249 | { "balance" := bal 250 | , "guard" := g } 251 | { "account" : account 252 | , "balance" : bal 253 | , "guard": g }) 254 | ) 255 | 256 | (defun rotate:string (account:string new-guard:guard) 257 | (with-capability (ROTATE account) 258 | (with-read coin-table account 259 | { "guard" := old-guard } 260 | 261 | (enforce-guard old-guard) 262 | 263 | (update coin-table account 264 | { "guard" : new-guard } 265 | ))) 266 | ) 267 | 268 | 269 | (defun precision:integer 270 | () 271 | MINIMUM_PRECISION) 272 | 273 | (defun transfer:string (sender:string receiver:string amount:decimal) 274 | @model [ (property conserves-mass) 275 | (property (> amount 0.0)) 276 | (property (valid-account sender)) 277 | (property (valid-account receiver)) 278 | (property (!= sender receiver)) ] 279 | 280 | (enforce (!= sender receiver) 281 | "sender cannot be the receiver of a transfer") 282 | 283 | (validate-account sender) 284 | (validate-account receiver) 285 | 286 | (enforce (> amount 0.0) 287 | "transfer amount must be positive") 288 | 289 | (enforce-unit amount) 290 | 291 | (with-capability (TRANSFER sender receiver amount) 292 | (debit sender amount) 293 | (with-read coin-table receiver 294 | { "guard" := g } 295 | 296 | (credit receiver g amount)) 297 | ) 298 | ) 299 | 300 | (defun transfer-create:string 301 | ( sender:string 302 | receiver:string 303 | receiver-guard:guard 304 | amount:decimal ) 305 | 306 | @model [ (property conserves-mass) ] 307 | 308 | (enforce (!= sender receiver) 309 | "sender cannot be the receiver of a transfer") 310 | 311 | (validate-account sender) 312 | (validate-account receiver) 313 | 314 | (enforce (> amount 0.0) 315 | "transfer amount must be positive") 316 | 317 | (enforce-unit amount) 318 | 319 | (with-capability (TRANSFER sender receiver amount) 320 | (debit sender amount) 321 | (credit receiver receiver-guard amount)) 322 | ) 323 | 324 | (defun coinbase:string (account:string account-guard:guard amount:decimal) 325 | @doc "Internal function for the initial creation of coins. This function \ 326 | \cannot be used outside of the coin contract." 327 | 328 | @model [ (property (valid-account account)) 329 | (property (> amount 0.0)) 330 | ] 331 | 332 | (validate-account account) 333 | (enforce-unit amount) 334 | 335 | (require-capability (COINBASE)) 336 | (with-capability (CREDIT account) 337 | (credit account account-guard amount)) 338 | ) 339 | 340 | (defun remediate:string (account:string amount:decimal) 341 | @doc "Allows for remediation transactions. This function \ 342 | \is protected by the REMEDIATE capability" 343 | @model [ (property (valid-account account)) 344 | (property (> amount 0.0)) 345 | ] 346 | 347 | (validate-account account) 348 | 349 | (enforce (> amount 0.0) 350 | "Remediation amount must be positive") 351 | 352 | (enforce-unit amount) 353 | 354 | (require-capability (REMEDIATE)) 355 | (with-read coin-table account 356 | { "balance" := balance } 357 | 358 | (enforce (<= amount balance) "Insufficient funds") 359 | 360 | (update coin-table account 361 | { "balance" : (- balance amount) } 362 | )) 363 | ) 364 | 365 | (defpact fund-tx (sender:string miner:string miner-guard:guard total:decimal) 366 | @doc "'fund-tx' is a special pact to fund a transaction in two steps, \ 367 | \with the actual transaction transpiring in the middle: \ 368 | \ \ 369 | \ 1) A buying phase, debiting the sender for total gas and fee, yielding \ 370 | \ TX_MAX_CHARGE. \ 371 | \ 2) A settlement phase, resuming TX_MAX_CHARGE, and allocating to the \ 372 | \ coinbase account for used gas and fee, and sender account for bal- \ 373 | \ ance (unused gas, if any)." 374 | 375 | @model [ (property (> total 0.0)) 376 | (property (valid-account sender)) 377 | (property (valid-account miner)) 378 | ;(property conserves-mass) not supported yet 379 | ] 380 | 381 | (step (buy-gas sender total)) 382 | (step (redeem-gas miner miner-guard sender total)) 383 | ) 384 | 385 | (defun debit:string (account:string amount:decimal) 386 | @doc "Debit AMOUNT from ACCOUNT balance" 387 | 388 | @model [ (property (> amount 0.0)) 389 | (property (valid-account account)) 390 | ] 391 | 392 | (validate-account account) 393 | 394 | (enforce (> amount 0.0) 395 | "debit amount must be positive") 396 | 397 | (enforce-unit amount) 398 | 399 | (require-capability (DEBIT account)) 400 | (with-read coin-table account 401 | { "balance" := balance } 402 | 403 | (enforce (<= amount balance) "Insufficient funds") 404 | 405 | (update coin-table account 406 | { "balance" : (- balance amount) } 407 | )) 408 | ) 409 | 410 | 411 | (defun credit:string (account:string guard:guard amount:decimal) 412 | @doc "Credit AMOUNT to ACCOUNT balance" 413 | 414 | @model [ (property (> amount 0.0)) 415 | (property (valid-account account)) 416 | ] 417 | 418 | (validate-account account) 419 | 420 | (enforce (> amount 0.0) "credit amount must be positive") 421 | (enforce-unit amount) 422 | 423 | (require-capability (CREDIT account)) 424 | (with-default-read coin-table account 425 | { "balance" : 0.0, "guard" : guard } 426 | { "balance" := balance, "guard" := retg } 427 | ; we don't want to overwrite an existing guard with the user-supplied one 428 | (enforce (= retg guard) 429 | "account guards do not match") 430 | 431 | (write coin-table account 432 | { "balance" : (+ balance amount) 433 | , "guard" : retg 434 | }) 435 | )) 436 | 437 | 438 | (defschema crosschain-schema 439 | @doc "Schema for yielded value in cross-chain transfers" 440 | receiver:string 441 | receiver-guard:guard 442 | amount:decimal) 443 | 444 | (defpact transfer-crosschain:string 445 | ( sender:string 446 | receiver:string 447 | receiver-guard:guard 448 | target-chain:string 449 | amount:decimal ) 450 | 451 | @model [ (property (> amount 0.0)) 452 | (property (valid-account sender)) 453 | (property (valid-account receiver)) 454 | ] 455 | 456 | (step 457 | (with-capability (DEBIT sender) 458 | 459 | (validate-account sender) 460 | (validate-account receiver) 461 | 462 | (enforce (!= "" target-chain) "empty target-chain") 463 | (enforce (!= (at 'chain-id (chain-data)) target-chain) 464 | "cannot run cross-chain transfers to the same chain") 465 | 466 | (enforce (> amount 0.0) 467 | "transfer quantity must be positive") 468 | 469 | (enforce-unit amount) 470 | 471 | ;; step 1 - debit delete-account on current chain 472 | (debit sender amount) 473 | 474 | (let 475 | ((crosschain-details:object{crosschain-schema} 476 | { "receiver" : receiver 477 | , "receiver-guard" : receiver-guard 478 | , "amount" : amount 479 | })) 480 | (yield crosschain-details target-chain) 481 | ))) 482 | 483 | (step 484 | (resume 485 | { "receiver" := receiver 486 | , "receiver-guard" := receiver-guard 487 | , "amount" := amount 488 | } 489 | 490 | ;; step 2 - credit create account on target chain 491 | (with-capability (CREDIT receiver) 492 | (credit receiver receiver-guard amount)) 493 | )) 494 | ) 495 | 496 | 497 | ; -------------------------------------------------------------------------- 498 | ; Coin allocations 499 | 500 | (defschema allocation-schema 501 | @doc "Genesis allocation registry" 502 | ;@model [ (invariant (>= balance 0.0)) ] 503 | 504 | balance:decimal 505 | date:time 506 | guard:guard 507 | redeemed:bool) 508 | 509 | (deftable allocation-table:{allocation-schema}) 510 | 511 | (defun create-allocation-account 512 | ( account:string 513 | date:time 514 | keyset-ref:string 515 | amount:decimal 516 | ) 517 | 518 | @doc "Add an entry to the coin allocation table. This function \ 519 | \also creates a corresponding empty coin contract account \ 520 | \of the same name and guard. Requires GENESIS capability. " 521 | 522 | @model [ (property (valid-account account)) ] 523 | 524 | (require-capability (GENESIS)) 525 | 526 | (validate-account account) 527 | (enforce (>= amount 0.0) 528 | "allocation amount must be non-negative") 529 | 530 | (enforce-unit amount) 531 | 532 | (let 533 | ((guard:guard (keyset-ref-guard keyset-ref))) 534 | 535 | (create-account account guard) 536 | 537 | (insert allocation-table account 538 | { "balance" : amount 539 | , "date" : date 540 | , "guard" : guard 541 | , "redeemed" : false 542 | }))) 543 | 544 | (defun release-allocation 545 | ( account:string ) 546 | 547 | @doc "Release funds associated with allocation ACCOUNT into main ledger. \ 548 | \ACCOUNT must already exist in main ledger. Allocation is deactivated \ 549 | \after release." 550 | @model [ (property (valid-account account)) ] 551 | 552 | (validate-account account) 553 | 554 | (with-read allocation-table account 555 | { "balance" := balance 556 | , "date" := release-time 557 | , "redeemed" := redeemed 558 | , "guard" := guard 559 | } 560 | 561 | (let ((curr-time:time (at 'block-time (chain-data)))) 562 | 563 | (enforce (not redeemed) 564 | "allocation funds have already been redeemed") 565 | 566 | (enforce 567 | (>= curr-time release-time) 568 | (format "funds locked until {}. current time: {}" [release-time curr-time])) 569 | 570 | (enforce-guard guard) 571 | 572 | (with-capability (CREDIT account) 573 | (credit account guard balance) 574 | 575 | (update allocation-table account 576 | { "redeemed" : true 577 | , "balance" : 0.0 578 | }) 579 | 580 | "Allocation successfully released to main ledger") 581 | ))) 582 | 583 | ) 584 | -------------------------------------------------------------------------------- /pact/dapp-contracts/coin-contract/coin.repl: -------------------------------------------------------------------------------- 1 | ;; Enable the table gas model 2 | (env-gasmodel "table") 3 | (env-gaslimit 30000) 4 | 5 | (begin-tx) 6 | (env-gas 0) (env-gaslog) 7 | (load "fungible-v2.pact") 8 | (env-gaslog) 9 | (expect 10 | "Gas cost of loading fungible contract" 11 | 840 (env-gas)) 12 | 13 | (env-gas 0) (env-gaslog) 14 | (load "coin.pact") 15 | (env-gaslog) 16 | (expect 17 | "Gas cost of loading coin contract" 18 | 3301 (env-gas)) 19 | 20 | (create-table coin.coin-table) 21 | (create-table coin.allocation-table) 22 | 23 | (commit-tx) 24 | 25 | (verify 'coin) 26 | 27 | ;; Account creation and account details unit tests 28 | 29 | (begin-tx) 30 | (env-data { "emily" : ["keys1"], "doug": ["keys2"], "stuart": ["keys3"] }) 31 | (env-keys ["keys1", "keys2", "keys3", "keys4"]) 32 | (define-keyset 'emily (read-keyset "emily")) 33 | (define-keyset 'doug (read-keyset "doug")) 34 | (define-keyset 'stuart (read-keyset "stuart")) 35 | 36 | (env-gas 0) (env-gaslog) 37 | (use coin) 38 | (env-gaslog) 39 | (expect 40 | "Gas cost of using the coin contract" 41 | 2 (env-gas)) 42 | 43 | ;; account balance for emily does not exist, because account does not exist yet 44 | (expect-failure 45 | "account does not exist yet" 46 | (get-balance 'emily)) 47 | 48 | ;; create accounts should succeed and initialize with correct amounts and guards 49 | (env-gas 0) (env-gaslog) 50 | (create-account 'emily (read-keyset 'emily)) 51 | (env-gaslog) 52 | (expect 53 | "Gas cost of coin contract account creation" 54 | 186 (env-gas)) 55 | 56 | (create-account 'doug (read-keyset 'doug)) 57 | 58 | 59 | ; accounts conform to account structure 60 | (expect-failure 61 | "non-latin1+ascii account names fail to create" 62 | "charset" 63 | (create-account "emilyπ" (read-keyset 'emily))) 64 | 65 | (expect-failure 66 | "empty account names fail to create" 67 | "min length" 68 | (create-account "" (read-keyset 'doug))) 69 | 70 | (expect-failure 71 | "account names not >= 3 chars fail" 72 | "min length" 73 | (create-account "jo" (read-keyset 'stuart))) 74 | 75 | (expect-failure 76 | "account names not <= 256 chars fail" 77 | "max length" 78 | (create-account 79 | "Before getting down to business, let us ask why it should be that category theory has such far-reaching applications. \ 80 | \Well, we said that it's the abstract theory of functions; so the answer is simply this: Functions are everywhere! \ 81 | \And everywhere that functions are, there are categories. Indeed, the subject might better have been called abstract \ 82 | \function theory, or perhaps even better: archery." 83 | (read-keyset 'emily))) 84 | 85 | ; check account balances for newly created accounts 86 | (env-gas 0) (env-gaslog) 87 | (expect 88 | "initial balance at 0.0" 89 | 0.0 90 | (get-balance 'emily)) 91 | (env-gaslog) 92 | (expect 93 | "Gas cost of querying an account's balance" 94 | 16 (env-gas)) 95 | 96 | ; account information checks out for new accounts 97 | (env-gas 0) (env-gaslog) 98 | (expect 99 | "details reflects the correct balance and guard information" 100 | {"account" : "doug", "balance": 0.0, "guard": (read-keyset 'doug)} 101 | (details 'doug)) 102 | (env-gaslog) 103 | (expect 104 | "Gas cost of querying the details of an account" 105 | 17 (env-gas)) 106 | 107 | (commit-tx) 108 | 109 | ;; credits + debits should succeed. Both should reflect the correct balance 110 | 111 | (begin-tx) 112 | 113 | (use coin) 114 | 115 | ; w/o capability 116 | (expect-failure 117 | "direct call to credit fails" 118 | "not granted" 119 | (credit 'emily (read-keyset 'emily) 1.0)) 120 | 121 | (expect-failure 122 | "direct call to debit fails" 123 | "not granted" 124 | (debit 'emily 1.0)) 125 | 126 | (env-gas 0) (env-gaslog) 127 | (test-capability (DEBIT 'emily)) 128 | (env-gaslog) 129 | (expect 130 | "Gas cost of testing the DEBIT capability" 131 | 26 (env-gas)) 132 | 133 | ; debit tests 134 | (expect-failure 135 | "debit not > 0.0 quantities fail fast" 136 | "must be positive" 137 | (debit 'emily 0.0)) 138 | 139 | (expect-failure 140 | "debit not > 0.0 quantities fail fast" 141 | "must be positive" 142 | (debit 'emily (- 1.0))) 143 | 144 | (expect-failure 145 | "debit from account with 0.0 in it yields failure" 146 | "Insufficient funds" 147 | (debit 'emily 1.0)) 148 | 149 | (expect-failure 150 | "cannot debit to poorly formatted accounts: charset" 151 | "charset" 152 | (debit "emilyπ" 1.0)) 153 | 154 | (expect-failure 155 | "cannot debit to poorly formatted accounts: min length" 156 | "min length" 157 | (debit "l" 1.0)) 158 | 159 | (expect-failure 160 | "cannot debit to poorly formatted accounts: max length" 161 | "max length" 162 | (debit "a mathematical object X is best thought of in the context of a category surrounding it, \ 163 | \and is determined by the network of relations it enjoys with all the objects of that category. \ 164 | \Moreover, to understand X it might be more germane to deal directly with the functor representing it" 1.0)) 165 | 166 | ; credit tests 167 | (test-capability (CREDIT "emily")) 168 | (credit 'emily (read-keyset 'emily) 1.0) 169 | 170 | (expect 171 | "account balance reflects credit" 172 | 1.0 173 | (get-balance 'emily)) 174 | 175 | (expect-failure 176 | "cannot credit to poorly formatted accounts: charset" 177 | "charset" 178 | (credit "emilyπ" (read-keyset 'emily) 1.0)) 179 | 180 | (expect-failure 181 | "cannot credit to poorly formatted accounts: min length" 182 | "min length" 183 | (credit "l" (read-keyset 'emily) 1.0)) 184 | 185 | (expect-failure 186 | "cannot credit to poorly formatted accounts: max length" 187 | "max length" 188 | (credit "The aim of theory really is, to a great extent, that of systematically organizing past experience in such a way that the next generation, our students and their students and so on, will be able to absorb the essential aspects in as painless a way as possible, and this is the only way in which you can go on cumulatively building up any kind of scientific activity without eventually coming to a dead end." (read-keyset 'emily) 1.0)) 189 | 190 | (test-capability (DEBIT "emily")) 191 | (debit 'emily 1.0) 192 | 193 | (expect 194 | "debiting funds now succeeds when there's enough funds" 195 | 0.0 196 | (get-balance 'emily)) 197 | 198 | ;; crediting non-existing accounts with guard should have supplied keys 199 | (test-capability (CREDIT "stuart")) 200 | 201 | (expect-failure 202 | "crediting trivial or negative funds fails fast" 203 | "positive" 204 | (credit 'stuart (read-keyset 'stuart) 0.0)) 205 | 206 | (expect-failure 207 | "crediting trivial or negative funds fails fast" 208 | "positive" 209 | (credit 'stuart (read-keyset 'stuart) (- 1.0))) 210 | 211 | (credit 'stuart (read-keyset 'stuart) 1.0) 212 | 213 | (expect 214 | "crediting funds to new account succeeds with correct balance" 215 | 1.0 216 | (get-balance 'stuart)) 217 | 218 | (expect-failure 219 | "cannot update a keyset for an existing account with wrong keyset" 220 | "account guards do not match" 221 | (credit 'stuart (read-keyset 'doug) 1.0)) 222 | 223 | (commit-tx) 224 | 225 | ;; fund-tx should require GAS capability in scope, and all funds should succeed 226 | ;; when available and reflect correct balances 227 | 228 | (begin-tx) 229 | 230 | (use coin) 231 | 232 | (expect-failure 233 | "fund-tx should fail when GAS is not in scope" 234 | "not granted: (coin.GAS)" 235 | (fund-tx 'emily 'doug (read-keyset 'doug) 1.0)) 236 | 237 | (test-capability (GAS)) 238 | (env-keys []) 239 | 240 | (expect-failure 241 | "fund-tx fails without signature" 242 | "Keyset failure" 243 | (fund-tx 'emily 'doug (read-keyset 'doug) 1.0)) 244 | 245 | (env-sigs [{"key": "keys1", "caps": [(TRANSFER "emily" "doug" 1.0)]}]) 246 | 247 | (expect-failure 248 | "fund-tx fails for no gas cap" 249 | "Keyset failure" 250 | (fund-tx 'emily 'doug (read-keyset 'doug) 1.0)) 251 | 252 | (expect-failure 253 | "fund-tx fails for trivial or negative quantities" 254 | "positive" 255 | (fund-tx 'emily 'doug (read-keyset 'doug) 0.0)) 256 | 257 | (expect-failure 258 | "fund-tx fails for trivial or negative quantities" 259 | "positive" 260 | (fund-tx 'emily 'doug (read-keyset 'doug) (- 1.0))) 261 | 262 | (env-sigs [{"key": "keys1", "caps": [(GAS)]}]) 263 | 264 | (expect-failure 265 | "fund-tx fails for insufficient funds" 266 | "Insufficient funds" 267 | (fund-tx 'emily 'doug (read-keyset 'doug) 1.0)) 268 | 269 | (env-keys ["keys1"]) 270 | (test-capability (CREDIT "emily")) 271 | (credit "emily" (read-keyset "emily") 3.0) 272 | 273 | (expect 274 | "fund-tx succeeds with gas cap" 275 | "Write succeeded" 276 | (fund-tx 'emily 'doug (read-keyset 'doug) 1.0)) 277 | 278 | (pact-state true) 279 | 280 | (env-sigs [{"key": "keys1", "caps": [(GAS),(TRANSFER "emily" "doug" 1.0)]}]) 281 | 282 | (expect 283 | "fund-tx succeeds with gas cap and other cap" 284 | "Write succeeded" 285 | (fund-tx 'emily 'doug (read-keyset 'doug) 1.0)) 286 | 287 | (pact-state true) 288 | 289 | (env-keys ["keys1"]) 290 | (expect 291 | "fund-tx succeeds with no caps" 292 | "Write succeeded" 293 | (fund-tx 'emily 'doug (read-keyset 'doug) 1.0)) 294 | 295 | (env-data { "fee" : 1.0 , "miner" : ["miner"] }) 296 | 297 | (expect 298 | "redeem-gas succeeds when fee is in scope" 299 | "Write succeeded" 300 | (redeem-gas "miner" (read-keyset "miner") "emily" 1.0)) 301 | 302 | (commit-tx) 303 | 304 | ;;; GAS (gas buying) tests 305 | 306 | (begin-tx) 307 | 308 | (use coin) 309 | 310 | ;; setup for next txs 311 | (env-data { "emily" : ["keys1"] }) 312 | (env-keys ["keys1"]) 313 | (test-capability (CREDIT "emily")) 314 | (credit "emily" (read-keyset "emily") 1.0) 315 | 316 | (env-data { "fee" : 0.0, "emily" : ["keys1"], "doug": ["keys2"] }) 317 | (env-keys ["keys1", "keys2"]) 318 | 319 | (test-capability (GAS)) 320 | 321 | (fund-tx 'emily 'doug (read-keyset 'doug) 1.0) 322 | 323 | (expect 324 | "doug should now have 0.0 coins having mined the tx due to 0 gas" 325 | 0.0 326 | (get-balance 'doug)) 327 | 328 | (expect 329 | "emily should now have 0.0 coins after paying miner" 330 | 0.0 331 | (get-balance 'emily)) 332 | 333 | (rollback-tx) 334 | 335 | ;; credit to seed next tests 336 | (begin-tx) 337 | 338 | (env-data { "fee" : 0.4, "emily" : ["keys1"], "doug": ["keys2"], "will": ["keys4"] }) 339 | (env-keys ["keys1", "keys2", "keys4"]) 340 | 341 | (test-capability (coin.CREDIT "emily")) 342 | (coin.credit "emily" (read-keyset "emily") 1.0) 343 | 344 | (commit-tx) 345 | 346 | ; Test capabilities interactions with transfers 347 | (begin-tx) 348 | 349 | (use coin) 350 | 351 | (env-data { "fee" : 0.4, "emily" : ["keys1"], "doug": ["keys2"], "will": ["keys4"] }) 352 | (env-keys ["keys1", "keys2", "keys4"]) 353 | 354 | (test-capability (GAS)) 355 | 356 | (coin.fund-tx 'emily 'doug (read-keyset 'doug) 1.0) 357 | 358 | (continue-pact 1) 359 | (expect 360 | "doug should now have 0.4 coins after mining 0.4 coin fee" 361 | 0.4 362 | (get-balance 'doug)) 363 | 364 | (expect 365 | "emily should now have 0.6 coins after paying for 0.4 coin fee" 366 | 0.6 367 | (get-balance 'emily)) 368 | 369 | ;; transfers should respect balances as intended 370 | (test-capability (CREDIT "emily")) 371 | (credit 'emily (read-keyset 'emily) 1.0) 372 | 373 | (commit-tx) 374 | (begin-tx) 375 | (use coin) 376 | 377 | (expect-failure 378 | "transfers of trivial or negative quantities fails fast" 379 | "positive" 380 | (transfer 'emily 'doug 0.0)) 381 | 382 | (expect-failure "can't install negative" 383 | "Positive amount" 384 | (test-capability (coin.TRANSFER "emily" "doug" -1.0))) 385 | 386 | (expect-failure 387 | "Transfer fails without managed cap installed" 388 | "not installed" 389 | (transfer 'emily 'doug 1.0)) 390 | 391 | (test-capability (coin.TRANSFER "emily" "doug" 1.0)) 392 | (env-gas 0) (env-gaslog) 393 | (expect 394 | "roundtrip 1.0 transfer succeeds" "Write succeeded" 395 | (transfer 'emily 'doug 1.0)) 396 | (env-gaslog) 397 | (expect 398 | "Gas cost of transfer" 399 | 484 (env-gas)) 400 | 401 | (expect-failure "emily->doug capability used up" 402 | "TRANSFER exceeded" 403 | (transfer 'emily 'doug 1.0)) 404 | 405 | (expect 406 | "emily now has 0.6 coins after transfer to 'doug" 407 | 0.6 408 | (get-balance 'emily)) 409 | 410 | (expect 411 | "doug now has 1.4 coins after transfer from 'emily" 412 | 1.4 413 | (get-balance 'doug)) 414 | 415 | (commit-tx) 416 | (begin-tx) 417 | (use coin) 418 | 419 | (test-capability (coin.TRANSFER "emily" "doug" 1.0)) 420 | (expect-failure 421 | "emily now has insufficient funds and cannot transfer" 422 | "Insufficient funds" 423 | (transfer 'emily 'doug 1.0)) 424 | 425 | (expect-failure "No account for will" 426 | "row not found" 427 | (get-balance 'will)) 428 | 429 | (test-capability (TRANSFER 'doug 'will 1.0)) 430 | (env-gas 0) (env-gaslog) 431 | (expect "transfer-create to new account succeeds" 432 | "Write succeeded" 433 | (transfer-create 'doug 'will (read-keyset 'will) 1.0)) 434 | (env-gaslog) 435 | (expect 436 | "Gas cost of transfer-create" 468 (env-gas)) 437 | 438 | (expect 439 | "doug now has 0.4 coins" 440 | 0.4 441 | (get-balance 'doug)) 442 | 443 | (expect 444 | "will now has 1.0 coins" 445 | 1.0 446 | (get-balance 'will)) 447 | 448 | (expect 449 | "details reflects the correct balance and guard information" 450 | {"account" : "will", "balance": 1.0, "guard": (read-keyset 'will)} 451 | (details 'will)) 452 | 453 | ;; coinbase should fail when 'COINBASE' capability is not in scope 454 | ;; and should magically create tokens for users and reflect correct balance 455 | 456 | (commit-tx) 457 | (begin-tx) 458 | (use coin) 459 | 460 | (expect-failure 461 | "coinbase fails when capability is not in scope" 462 | "not granted" 463 | (coinbase 'emily (read-keyset 'emily) 0.0)) 464 | 465 | (test-capability (COINBASE)) 466 | 467 | (coinbase 'emily (read-keyset 'emily) 1.0) 468 | 469 | (test-capability (COINBASE)) 470 | (expect-failure 471 | "coinbasing trivial or negative amounts fails fast" 472 | "positive" 473 | (coinbase 'emily (read-keyset 'emily) 0.0)) 474 | 475 | (test-capability (COINBASE)) 476 | (expect-failure 477 | "coinbasing trivial or negative amounts fails fast" 478 | "positive" 479 | (coinbase 'emily (read-keyset 'emily) (- 1.0))) 480 | 481 | (expect 482 | "after coinbase, emily should have 1.6 coins in its account" 483 | 1.6 484 | (get-balance 'emily)) 485 | 486 | 487 | (env-data { "miner2": ["miner2"] }) 488 | 489 | (expect-failure "no account for miner2" 490 | "row not found" 491 | (get-balance 'miner2)) 492 | 493 | (test-capability (COINBASE)) 494 | (coinbase 'miner2 (read-keyset 'miner2) 1.0) 495 | 496 | (expect 497 | "coinbase should create accounts and credit them some amount" 498 | 1.0 (get-balance 'miner2)) 499 | 500 | (commit-tx) 501 | 502 | ;; test burn-creates on new chains 503 | 504 | (begin-tx) 505 | 506 | (use coin) 507 | (env-chain-data { "chain-id" : "0" }) 508 | (env-hash (hash "burn-create")) 509 | (env-data {"doug": ["keys2"]}) 510 | 511 | (expect-failure 512 | "cross-chain transfers fail for trivial or negative quantities" 513 | "positive" 514 | (transfer-crosschain 'emily 'doug (read-keyset 'doug) "1" 0.0)) 515 | 516 | (expect-failure 517 | "cross-chain transfers fail for trivial or negative quantities" 518 | "positive" 519 | (transfer-crosschain 'emily 'doug (read-keyset 'doug) "1" (- 1.0))) 520 | 521 | (expect 522 | "burn side of cross-chain transfers succeed" 523 | "success" 524 | (let 525 | ((p 526 | (transfer-crosschain 'emily 'doug (read-keyset 'doug) "1" 1.0))) 527 | "success")) 528 | 529 | ; make sure chain-id is enforced in the yield 530 | (expect-failure 531 | "create side of cross-chain transfer fails yield on wrong chain" 532 | "does not match (chain" 533 | (continue-pact 1 false (hash "burn-create") 534 | { "create-account": 'doug 535 | , "create-account-guard": (read-keyset 'doug) 536 | , "quantity": 1.0 537 | })) 538 | 539 | ; successful path 540 | (env-chain-data { "chain-id" : "1" }) 541 | 542 | (expect 543 | "create side of cross-chain transfer succeeds" 544 | "Write succeeded" 545 | (continue-pact 1 false (hash "burn-create") 546 | { "receiver": 'doug 547 | , "receiver-guard": (read-keyset 'doug) 548 | , "amount": 1.0 549 | })) 550 | 551 | ; double spends are disallowed by construction 552 | (expect-failure 553 | "cross-chain transfer pact prevents double spends" 554 | "pact completed" 555 | (continue-pact 1 false (hash "burn-create"))) 556 | 557 | ; account guard rotation 558 | (expect 559 | "account info for 'emily uses 'emily keyset pre-rotation" 560 | "Account: 0.6 Guard: KeySet {keys: [keys1],pred: keys-all}" 561 | (let 562 | ((i (details 'emily))) 563 | (format "Account: {} Guard: {}" [(at 'balance i) (at 'guard i)]))) 564 | 565 | ; account details will now feature rotated guard 566 | (env-keys ["keys1", "keys2"]) 567 | (expect-failure 568 | "guard rotation fails when ROTATE is not scoped" 569 | "Managed capability not installed" 570 | (rotate 'emily (read-keyset 'doug))) 571 | 572 | (env-sigs [{'key: "keys1", 'caps: [(coin.ROTATE "emily")]}]) 573 | (install-capability (ROTATE "emily")) 574 | 575 | (expect 576 | "guard rotation succeeds when ROTATE is scoped" 577 | "Write succeeded" 578 | (rotate 'emily (read-keyset 'doug))) 579 | 580 | (expect 581 | "account info for 'emily uses 'doug keyset after rotation" 582 | "Account: 0.6 Guard: KeySet {keys: [keys2],pred: keys-all}" 583 | (let ((i (details 'emily))) 584 | (format "Account: {} Guard: {}" [(at 'balance i) (at 'guard i)]))) 585 | 586 | (commit-tx) 587 | 588 | ;; cover enforce-unit 589 | 590 | (begin-tx) 591 | 592 | (use coin) 593 | (module T G 594 | (defcap G () true) 595 | (defconst UNIT_BAD 0.0000000000001) 596 | (defconst UNIT_GOOD 0.000000000001)) 597 | 598 | (expect 599 | "valid unit" 600 | true 601 | (enforce-unit 1.234)) 602 | 603 | (expect-failure 604 | "invalid precision" 605 | "minimum precision" 606 | (enforce-unit 1.1234567890123)) 607 | 608 | (expect-failure 609 | "too small" 610 | "minimum precision" 611 | (enforce-unit UNIT_BAD)) 612 | 613 | (expect 614 | "min value ok" 615 | true 616 | (enforce-unit UNIT_GOOD)) 617 | 618 | (env-keys ["keys1", "keys2"]) 619 | 620 | (test-capability (TRANSFER "emily" "doug" UNIT_GOOD)) 621 | ;; Transfer 622 | (expect 623 | "min transfer ok" 624 | "Write succeeded" 625 | (transfer 'emily 'doug UNIT_GOOD)) 626 | 627 | (expect-failure 628 | "bad transfer fails" 629 | "precision" 630 | (transfer 'emily 'doug UNIT_BAD)) 631 | 632 | ;; transfer-create 633 | (expect-failure 634 | "TRANSFER capability fails when paid amount is exceeded" 635 | "TRANSFER exceeded" 636 | (transfer-create 'emily 'doug (read-keyset 'doug) UNIT_GOOD)) 637 | (commit-tx) 638 | (begin-tx) 639 | (use coin) 640 | (use T) 641 | 642 | (test-capability (TRANSFER "emily" "doug" (* 2 UNIT_GOOD))) 643 | (expect 644 | "min transfer-create ok" 645 | "Write succeeded" 646 | (transfer-create 'emily 'doug (read-keyset 'doug) UNIT_GOOD)) 647 | 648 | (expect-failure 649 | "bad transfer-create fails" 650 | "minimum precision" 651 | (transfer-create 'emily 'doug (read-keyset 'doug) UNIT_BAD)) 652 | 653 | ;;transfer-crosschain (step 0 only covered) 654 | 655 | (expect 656 | "min transfer-crosschain step 1 succeeds" 657 | "success" 658 | (let ((s "success")) 659 | (transfer-crosschain 'emily 'doug (read-keyset 'doug) "chain" UNIT_GOOD) 660 | s)) 661 | 662 | (pact-state true) 663 | 664 | (expect-failure 665 | "bad transfer-crosschain fails" 666 | "minimum precision" 667 | (transfer-crosschain 'emily 'doug (read-keyset 'doug) "chain" UNIT_BAD)) 668 | 669 | ;;coinbase 670 | (test-capability (COINBASE)) 671 | (expect 672 | "min coinbase succeeds" 673 | "Write succeeded" 674 | (coinbase 'doug (read-keyset 'doug) UNIT_GOOD)) 675 | 676 | (expect-failure 677 | "bad coinbase fails" 678 | "minimum precision" 679 | (coinbase 'doug (read-keyset 'doug) UNIT_BAD)) 680 | 681 | ;;buy-gas 682 | (test-capability (GAS)) 683 | (expect 684 | "min buy-gas succeeds" 685 | "Write succeeded" 686 | (buy-gas 'emily UNIT_GOOD)) 687 | 688 | (expect-failure 689 | "bad buy-gas fails" 690 | "minimum precision" 691 | (buy-gas 'emily UNIT_BAD)) 692 | 693 | ;;redeem-gas 694 | (env-data { "fee" : UNIT_GOOD, "doug": ["keys2"]}) 695 | 696 | (expect 697 | "min redeem-gas succeeds" 698 | "Write succeeded" 699 | (redeem-gas 'doug (read-keyset 'doug) 'emily UNIT_GOOD)) 700 | 701 | (env-data { "fee" : UNIT_BAD, "doug": ["keys2"]}) 702 | 703 | (expect-failure 704 | "bad redeem-gas fails" 705 | "minimum precision" 706 | (redeem-gas 'doug (read-keyset 'doug) 'emily UNIT_BAD)) 707 | 708 | (commit-tx) 709 | 710 | ;; Coin allocation tests 711 | 712 | (begin-tx) 713 | 714 | (use coin) 715 | 716 | ; account creation 717 | 718 | (expect-failure 719 | "allocation account creation only occurs at genesis" 720 | "not granted: (coin.GENESIS)" 721 | (create-allocation-account "brandon" (time "1900-10-31T00:00:00Z") "brandon" 200000.0)) 722 | 723 | (test-capability (GENESIS)) 724 | 725 | (expect-failure 726 | "all allocation amounts must be positive" 727 | "non-negative" 728 | (create-allocation-account "brandon" (time "1900-10-31T00:00:00Z") "brandon" -200000.0)) 729 | 730 | (expect-failure 731 | "all allocation accounts must satisfy coin contract account min chars" 732 | "min length" 733 | (create-allocation-account "br" (time "1900-10-31T00:00:00Z") "brandon" 200000.0)) 734 | 735 | (expect-failure 736 | "all allocation accounts must satisfy coin contract account max chars" 737 | "max length" 738 | (create-allocation-account 739 | "There he met Saunders Mac Lane. Mac Lane, then visiting Paris, was anxious \ 740 | \to learn from Yoneda, and commenced an interview with Yoneda in a cafe at \ 741 | \Gare du Nord. The interview was continued on Yoneda's train until its \ 742 | \departure. In its course, Mac Lane learned about the lemma and \ 743 | \subsequently baptized it." 744 | (time "1900-10-31T00:00:00Z") "brandon" 200000.0)) 745 | 746 | (expect-failure 747 | "account creation fails when no keyset corresponds with keyset ref" 748 | "Keyset reference" 749 | (create-allocation-account "brandon" (time "2020-10-31T00:00:00Z") "brandon" 200000.0)) 750 | 751 | ; successful keyset refs require defined keyset 752 | 753 | (env-data { "brandon" : ["brandon"]}) 754 | (define-keyset "brandon" (read-keyset "brandon")) 755 | 756 | (expect 757 | "allocating coin accounts succeeds" 758 | "Write succeeded" 759 | (create-allocation-account "brandon" (time "2020-10-31T00:00:00Z") "brandon" 10.0)) 760 | 761 | (expect 762 | "allocation creates empty account" 763 | {"account" : "brandon", "balance":0.0, "guard":(keyset-ref-guard "brandon")} 764 | (details "brandon")) 765 | 766 | ; release-allocation 767 | 768 | (expect-failure 769 | "allocation coins fails since release date is not >= current time" 770 | "funds locked until" 771 | (release-allocation "brandon")) 772 | 773 | (env-chain-data { "block-time" : (time "2020-10-31T00:00:00Z") }) 774 | 775 | (expect-failure 776 | "allocation release fails when keys are not in scope" 777 | "Keyset failure" 778 | (release-allocation "brandon")) 779 | 780 | (env-keys ["brandon"]) 781 | 782 | (expect 783 | "successfully allocates funds for correct amounts and date" 784 | "Allocation successfully released to main ledger" 785 | (release-allocation "brandon")) 786 | 787 | (expect-failure 788 | "releases fail when funds have been redeemed" 789 | "funds have already been redeemed" 790 | (release-allocation "brandon")) 791 | 792 | (expect 793 | "brandon has 10 coins released to his account in coin contract" 794 | 10.0 (get-balance 'brandon)) 795 | 796 | (commit-tx) 797 | 798 | (begin-tx) 799 | 800 | (use coin) 801 | 802 | (expect-failure 803 | "gas-only fails without the presence of GAS" 804 | "not granted: (coin.GAS)" 805 | (gas-only)) 806 | 807 | (expect-failure 808 | "gas-guard fails when GAS is not present" 809 | "Failure: Tx Failed: Enforce either the presence of a GAS cap or keyset" 810 | (gas-guard (keyset-ref-guard "emily"))) 811 | 812 | (test-capability (GAS)) 813 | 814 | (expect 815 | "gas-only succeeds with the presence of GAS" 816 | true 817 | (gas-only)) 818 | 819 | (expect 820 | "gas-guard succeeds when one of Gas or keyset are present" 821 | true 822 | (gas-guard (keyset-ref-guard "emily"))) 823 | 824 | (commit-tx) 825 | 826 | (begin-tx) 827 | 828 | (use coin) 829 | 830 | (env-data { "bez" : ["bez"] }) 831 | (env-keys ["bez"]) 832 | (define-keyset "bez" (read-keyset "bez")) 833 | 834 | (expect 835 | "gas-guard succeeds when GAS not present, but keyset is" 836 | true 837 | (gas-guard (keyset-ref-guard "bez"))) 838 | 839 | (commit-tx) 840 | 841 | (begin-tx) 842 | 843 | (use coin) 844 | 845 | (expect-failure 846 | "Remediations fail without the presence of REMEDIATE" 847 | "not granted" 848 | (remediate "brandon" 1.0)) 849 | 850 | (test-capability (REMEDIATE)) 851 | 852 | (expect 853 | "Remediations succeed in the presence of REMEDIATE" 854 | "Write succeeded" 855 | (remediate "brandon" 1.0)) 856 | 857 | (expect-failure 858 | "Cannot remediate negative amounts" 859 | "Remediation amount must be positive" 860 | (remediate "brandon" -1.0)) 861 | 862 | (expect-failure 863 | "Cannot remediate amounts that don't conform to unit standards" 864 | "Amount violates minimum precision" 865 | (remediate "brandon" 1.0000000000001)) 866 | 867 | (expect-failure 868 | "Cannot remediate accounts that are too small" 869 | "Account name does not conform to the min length requirement" 870 | (remediate "br" 1.0)) 871 | 872 | (expect-failure 873 | "Cannot remediate accounts that are too large" 874 | "Account name does not conform to the max length requirement" 875 | (remediate "Classically, category theory is a useful tool not so much because of the light it sheds on any particular mathematical discipline but instead because categories are so ubiquitous: mathematical objects in many different settings (sets, groups, smooth manifolds, and so on) can be organized into categories. Moreover, many elementary mathematical concepts can be described in purely categorical terms and therefore make sense in each of these settings." 1.0)) 876 | -------------------------------------------------------------------------------- /pact/dapp-contracts/coin-contract/fungible-v2.pact: -------------------------------------------------------------------------------- 1 | (interface fungible-v2 2 | 3 | " Standard for fungible coins and tokens as specified in KIP-0002. " 4 | 5 | ; ---------------------------------------------------------------------- 6 | ; Schema 7 | 8 | (defschema account-details 9 | @doc "Schema for results of 'account' operation." 10 | @model [ (invariant (!= "" sender)) ] 11 | 12 | account:string 13 | balance:decimal 14 | guard:guard) 15 | 16 | 17 | ; ---------------------------------------------------------------------- 18 | ; Caps 19 | 20 | (defcap TRANSFER:bool 21 | ( sender:string 22 | receiver:string 23 | amount:decimal 24 | ) 25 | @doc " Managed capability sealing AMOUNT for transfer from SENDER to \ 26 | \ RECEIVER. Permits any number of transfers up to AMOUNT." 27 | @managed amount TRANSFER-mgr 28 | ) 29 | 30 | (defun TRANSFER-mgr:decimal 31 | ( managed:decimal 32 | requested:decimal 33 | ) 34 | @doc " Manages TRANSFER AMOUNT linearly, \ 35 | \ such that a request for 1.0 amount on a 3.0 \ 36 | \ managed quantity emits updated amount 2.0." 37 | ) 38 | 39 | ; ---------------------------------------------------------------------- 40 | ; Functionality 41 | 42 | 43 | (defun transfer:string 44 | ( sender:string 45 | receiver:string 46 | amount:decimal 47 | ) 48 | @doc " Transfer AMOUNT between accounts SENDER and RECEIVER. \ 49 | \ Fails if either SENDER or RECEIVER does not exist." 50 | @model [ (property (> amount 0.0)) 51 | (property (!= sender "")) 52 | (property (!= receiver "")) 53 | (property (!= sender receiver)) 54 | ] 55 | ) 56 | 57 | (defun transfer-create:string 58 | ( sender:string 59 | receiver:string 60 | receiver-guard:guard 61 | amount:decimal 62 | ) 63 | @doc " Transfer AMOUNT between accounts SENDER and RECEIVER. \ 64 | \ Fails if SENDER does not exist. If RECEIVER exists, guard \ 65 | \ must match existing value. If RECEIVER does not exist, \ 66 | \ RECEIVER account is created using RECEIVER-GUARD. \ 67 | \ Subject to management by TRANSFER capability." 68 | @model [ (property (> amount 0.0)) 69 | (property (!= sender "")) 70 | (property (!= receiver "")) 71 | (property (!= sender receiver)) 72 | ] 73 | ) 74 | 75 | (defpact transfer-crosschain:string 76 | ( sender:string 77 | receiver:string 78 | receiver-guard:guard 79 | target-chain:string 80 | amount:decimal 81 | ) 82 | @doc " 2-step pact to transfer AMOUNT from SENDER on current chain \ 83 | \ to RECEIVER on TARGET-CHAIN via SPV proof. \ 84 | \ TARGET-CHAIN must be different than current chain id. \ 85 | \ First step debits AMOUNT coins in SENDER account and yields \ 86 | \ RECEIVER, RECEIVER_GUARD and AMOUNT to TARGET-CHAIN. \ 87 | \ Second step continuation is sent into TARGET-CHAIN with proof \ 88 | \ obtained from the spv 'output' endpoint of Chainweb. \ 89 | \ Proof is validated and RECEIVER is credited with AMOUNT \ 90 | \ creating account with RECEIVER_GUARD as necessary." 91 | @model [ (property (> amount 0.0)) 92 | (property (!= sender "")) 93 | (property (!= receiver "")) 94 | (property (!= sender receiver)) 95 | (property (!= target-chain "")) 96 | ] 97 | ) 98 | 99 | (defun get-balance:decimal 100 | ( account:string ) 101 | " Get balance for ACCOUNT. Fails if account does not exist." 102 | ) 103 | 104 | (defun details:object{account-details} 105 | ( account: string ) 106 | " Get an object with details of ACCOUNT. \ 107 | \ Fails if account does not exist." 108 | ) 109 | 110 | (defun precision:integer 111 | () 112 | "Return the maximum allowed decimal precision." 113 | ) 114 | 115 | (defun enforce-unit:bool 116 | ( amount:decimal ) 117 | " Enforce minimum precision allowed for transactions." 118 | ) 119 | 120 | (defun create-account:string 121 | ( account:string 122 | guard:guard 123 | ) 124 | " Create ACCOUNT with 0.0 balance, with GUARD controlling access." 125 | ) 126 | 127 | (defun rotate:string 128 | ( account:string 129 | new-guard:guard 130 | ) 131 | " Rotate guard for ACCOUNT. Transaction is validated against \ 132 | \ existing guard before installing new guard. " 133 | ) 134 | 135 | ) 136 | -------------------------------------------------------------------------------- /pact/dapp-contracts/covid/README.md: -------------------------------------------------------------------------------- 1 | # COVID-19 Platform 2 | 3 | ![covid](dashboard.png) 4 | 5 | This contract is designed to provide covid-19 tracking platform on Kadena blockchain. 6 | 7 | ## Governance 8 | The contract is governed by a capability, "GOVERNANCE". The capability is guarded by the guard of the coin account, `covid-admin`. 9 | 10 | Learn more about Module Governance [here](https://pact-language.readthedocs.io/en/stable/pact-reference.html#generalized-module-governance) 11 | 12 | ## Tables 13 | The contract contains a `test-table-three` to store test results and a `printer-table-three` to store printing entity information. 14 | - **test-table-three** : `test-manufacturer` `test-model` `age-group` `gender` `country` `zipcode` `result` `patient-hash` `last-mod-time` `pub-key-init-bh` `test-init-bh` `test-end-bh` 15 | - **printer-table-three** : `authorized` `entity-name` `test-pub-keys` 16 | 17 | Learn more about Pact tables [here](https://pact-language.readthedocs.io/en/latest/pact-reference.html#deftable) 18 | 19 | 20 | ## Functions 21 | 22 | ### create-printing-entity 23 | - Function for Admin to create a Printing Entity: `pub-key``entity-name` 24 | ``` 25 | (create-printing-entity "some pub key" "some entity") 26 | ``` 27 | 28 | ### register-test 29 | - Function for a Printing Entity to register a public key on a chain: `printer-pub-key` `test-manufacturer` `test-model` `accts` 30 | 31 | ``` 32 | (register-test "somePublicKeyForPrinter0" "some manufacturer 0" "some model 0" [{"acct-name": "key-one", "ks-name": "ks1"},{"acct-name": "key-two", "ks-name": "ks2"}])) 33 | ``` 34 | 35 | ### administer-test 36 | - Function for a Registered Test to administer a test and write demographic info: `pub-key` `age-group` `gender` `country` `zipcode` `patient-hash` 37 | ``` 38 | (administer-test "randomPubKey" "0-10" "male" "USA" "11249" "someRandomHash") 39 | ``` 40 | 41 | ### end-test 42 | - Function for a Registered Test to end a test and write result: `pub-key` `result` 43 | 44 | Learn more about Pact functions [here](https://pact-language.readthedocs.io/en/latest/pact-reference.html#defun) 45 | 46 | ## Demo 47 | 48 | The Contract is deployed on Kadena Testnet, and is usable [here](https://covid19-dashboard.chainweb.com/) 49 | -------------------------------------------------------------------------------- /pact/dapp-contracts/covid/covid.pact: -------------------------------------------------------------------------------- 1 | (namespace "user") 2 | (module covid GOVERNANCE 3 | 4 | (use coin) 5 | 6 | ;; =============== 7 | ;; CONTRACT GOVERANCE: Only covid-admin account 8 | ;; =============== 9 | 10 | (defcap GOVERNANCE () 11 | "makes sure only admin account can update the smart contract" 12 | (enforce-guard (at 'guard (coin.details "covid-admin"))) 13 | ) 14 | 15 | ;; =============== 16 | ;; TABLES: test and pritning entity data columns 17 | ;; =============== 18 | 19 | (defschema test-schema 20 | ;; test manufacturer 21 | test-manufacturer:string 22 | ;; test model or manufacturer name for test 23 | test-model:string 24 | ;; patient age-group 25 | age-group:string 26 | ;; patient gender: male | female | other 27 | gender:string 28 | ;; patient country 29 | country:string 30 | ;; patient zip code 31 | zipcode:string 32 | ;; patient test result: positive | negative | inconclusive 33 | result:string 34 | ;; patient info hash 35 | patient-hash:string 36 | ;; last time record was modified 37 | last-mod-time:time 38 | ;; record public key initalization block height 39 | pub-key-init-bh:integer 40 | ;; test initialization (administering) block height 41 | test-init-bh:integer 42 | ;; test end (record result) block height 43 | test-end-bh:integer) 44 | 45 | (defschema printer-schema 46 | ;; if printer is authorized to print 47 | authorized:bool 48 | ;; name of printing entity (ex: manufacturer or authorized supplier) 49 | entity-name:string 50 | ;; list of all test label public keys priting entity has registered 51 | test-pub-keys:[string]) 52 | 53 | (defschema acct-info 54 | ;pub key of test 55 | acct-name:string 56 | ;name of keyset to read from env-data 57 | ks-name:string 58 | ) 59 | 60 | (deftable test-table-three:{test-schema}) 61 | (deftable printer-table-three:{printer-schema}) 62 | 63 | 64 | ;; =============== 65 | ;; CAPABILITIES: admin, printing entity, test 66 | ;; =============== 67 | 68 | (defcap ADMIN () 69 | "makes sure only admin account can approve new printing entities" 70 | (enforce-guard (at 'guard (coin.details "covid-admin"))) 71 | ) 72 | 73 | (defcap PRINTING-ENTITY (pub-key:string) 74 | "enforce printing entity coin account and active status" 75 | (enforce-guard (at 'guard (coin.details pub-key))) 76 | (with-read printer-table-three pub-key {"authorized" := authorized} 77 | (enforce authorized "printing entity no longer authorized to register tests")) 78 | ) 79 | 80 | (defcap REGISTERED-TEST (pub-key:string) 81 | "make sure the test keys match provided public key" 82 | (enforce-guard (at 'guard (coin.details pub-key))) 83 | ) 84 | 85 | ;; =============== 86 | ;; ADMIN ONLY FUNCTIONS: can create and blacklist printing entities 87 | ;; =============== 88 | 89 | (defun create-printing-entity (pub-key:string entity-name:string) 90 | @doc "ADMIN ONLY: Init new printing entity" 91 | (with-capability (ADMIN) 92 | (coin.create-account pub-key (read-keyset "ks")) 93 | (insert printer-table-three pub-key { 94 | "authorized": true, 95 | "entity-name": entity-name, 96 | "test-pub-keys": [] 97 | }) 98 | (format "Entity {} is now authorized to register and print test labels with pub-key={}" 99 | [entity-name, pub-key]) 100 | ) 101 | ) 102 | 103 | (defun blacklist-printing-entity (pub-key:string) 104 | @doc "ADMIN ONLY: Init new printing entity" 105 | (with-capability (ADMIN) 106 | (with-read printer-table-three pub-key { 107 | "entity-name":= entity-name, 108 | "authorized":= authorized} 109 | (enforce authorized "priting entity is already blacklisted") 110 | (update printer-table-three pub-key { 111 | "authorized": false}) 112 | (format "Entity {} is no longer authorized to print" 113 | [entity-name]) 114 | ) 115 | ) 116 | ) 117 | 118 | ;; =============== 119 | ;; PRINTING ENTITY ONLY FUNCTIONS: can register test to print labels 120 | ;; =============== 121 | 122 | 123 | (defun register-test-helper (printer-pub-key:string test-manufacturer:string test-model:string acct:object:{acct-info}) 124 | @doc "PRINTING ENTITY ONLY: facilitate mapping over the last parameter of the function" 125 | (require-capability (PRINTING-ENTITY printer-pub-key)) 126 | (coin.create-account (at "acct-name" acct) (read-keyset (at "ks-name" acct))) 127 | (insert test-table-three (at "acct-name" acct) { 128 | "test-manufacturer": test-manufacturer, 129 | "test-model": test-model, 130 | "age-group": "", 131 | "gender": "", 132 | "country": "", 133 | "zipcode": "", 134 | "result": "", 135 | "patient-hash": "", 136 | "last-mod-time": (at 'block-time (chain-data)), 137 | "pub-key-init-bh": (at 'block-height (chain-data)), 138 | "test-init-bh": 0, 139 | "test-end-bh": 0 140 | }) 141 | (with-read printer-table-three printer-pub-key { 142 | "test-pub-keys" := test-pub-keys} 143 | (update printer-table-three printer-pub-key { 144 | "test-pub-keys": (+ test-pub-keys [(at "acct-name" acct)]) 145 | }) 146 | ) 147 | ) 148 | 149 | 150 | (defun register-test (printer-pub-key:string test-manufacturer:string test-model:string accts:[object:{acct-info}]) 151 | @doc "PRINTING ENTITY ONLY: register the test public key on chain when printing a label" 152 | (with-capability (PRINTING-ENTITY printer-pub-key) 153 | (map (register-test-helper printer-pub-key test-manufacturer test-model) accts) 154 | (format 155 | "{} tests registered on chain by printer={}" 156 | [(length accts), printer-pub-key]) 157 | ) 158 | ) 159 | 160 | ;; =============== 161 | ;; TEST ONLY FUNCTIONS: called when test is handled by doctors through test dashboard 162 | ;; =============== 163 | 164 | (defun administer-test (pub-key:string age-group:string gender:string country:string zipcode:string patient-hash:string) 165 | @doc "REGISTED-TEST ONLY: administer a test and write demographic info" 166 | (with-capability (REGISTERED-TEST pub-key) 167 | (with-read test-table-three pub-key {"test-init-bh" := init-bh} 168 | (enforce (= init-bh 0) "this test has already been administered") 169 | (update test-table-three pub-key { 170 | "age-group": age-group, 171 | "gender": gender, 172 | "country": country, 173 | "zipcode": zipcode, 174 | "patient-hash": patient-hash, 175 | "last-mod-time": (at 'block-time (chain-data)), 176 | "test-init-bh": (at 'block-height (chain-data)) 177 | }) 178 | (format 179 | "Test with public key={} administered to patient={}" 180 | [pub-key, patient-hash]) 181 | ) 182 | ) 183 | ) 184 | 185 | (defun end-test (pub-key:string result:string) 186 | @doc "REGISTED-TEST ONLY: end a test and write result" 187 | (with-capability (REGISTERED-TEST pub-key) 188 | (with-read test-table-three pub-key { 189 | "test-end-bh" := end-bh, 190 | "test-init-bh" := init-bh} 191 | (enforce (!= init-bh 0) "this test has not yet been administered") 192 | (enforce (= end-bh 0) "this test has already been ended") 193 | (update test-table-three pub-key { 194 | "result": result, 195 | "last-mod-time": (at 'block-time (chain-data)), 196 | "test-end-bh": (at 'block-height (chain-data)) 197 | }) 198 | (format 199 | "Test with public key={} ended with result={}" 200 | [pub-key, result]) 201 | ) 202 | ) 203 | ) 204 | 205 | ;; =============== 206 | ;; READ FUNCTIONS: anyone can get test and printing entity info 207 | ;; =============== 208 | 209 | (defun get-all-test-keys () 210 | @doc "returns all test public keys" 211 | (keys test-table-three) 212 | ) 213 | 214 | (defun get-record:object{test-schema} (pub-key:string) 215 | @doc "gets data for a test by public key" 216 | (read test-table-three pub-key) 217 | ) 218 | 219 | (defun get-all-printing-entities () 220 | @doc "returns all printing entity pub keys" 221 | (keys printer-table-three) 222 | ) 223 | 224 | (defun get-priting-entity:object{printer-schema} (pub-key:string) 225 | @doc "get info of a particular printing entity" 226 | (read printer-table-three pub-key) 227 | ) 228 | 229 | 230 | ) 231 | 232 | ; (create-table test-table-three) 233 | ; (create-table printer-table-three) 234 | -------------------------------------------------------------------------------- /pact/dapp-contracts/covid/covid.repl: -------------------------------------------------------------------------------- 1 | ;; =============== 2 | ;; Setup 3 | ;; =============== 4 | (begin-tx) 5 | 6 | ;;Load Namespace Contract 7 | (env-data 8 | { 'ns-admin-keyset: ["admin"] 9 | , 'ns-operate-keyset: ["operate"] 10 | , 'ns-genesis-keyset: { "keys": [], "pred": "="} }) 11 | (load "../../dapp-contracts/namespaces/ns.pact") 12 | 13 | ;;Load Coin Contract 14 | (load "../../dapp-contracts/coin-contract/fungible-v2.pact") 15 | (load "../../dapp-contracts/coin-contract/coin.pact") 16 | (create-table coin.coin-table) 17 | 18 | ; Create r coin contract account 19 | (env-data { 'ks: ["adminKey"] }) 20 | (coin.create-account "covid-admin" (read-keyset "ks")) 21 | 22 | (env-keys ["adminKey"]) 23 | (load "covid.pact") 24 | ;(typecheck "user.covid" true) 25 | 26 | 27 | 28 | ; Create covid contract tables 29 | (env-keys ["somePublicKeyForr"]) 30 | (env-keys ["adminKey"]) 31 | (create-table test-table-three) 32 | (create-table printer-table-three) 33 | (commit-tx) 34 | 35 | 36 | ;; =============== 37 | ;; TEST: registering a printer 38 | ;; =============== 39 | (begin-tx) 40 | (env-keys []) 41 | (use user.covid) 42 | 43 | (expect-failure 44 | "non-admin account cannot register a new printing entity" 45 | "Keyset failure" 46 | (create-printing-entity "some pub key" "some entity")) 47 | 48 | ;must sign with admin key 49 | (env-keys ["adminKey"]) 50 | 51 | (env-data { 'ks: ["somePublicKeyForPrinter0"] }) 52 | (expect 53 | "admin can add printer" 54 | "Entity entity0 is now authorized to register and print test labels with pub-key=somePublicKeyForPrinter0" 55 | (create-printing-entity "somePublicKeyForPrinter0" "entity0")) 56 | 57 | 58 | (commit-tx) 59 | ;; =============== 60 | ;; TEST: registering a test with its pub key 61 | ;; =============== 62 | (begin-tx) 63 | (env-keys []) 64 | (env-keys ["adminKey"]) 65 | (use user.covid) 66 | 67 | (expect-failure 68 | "non-admin cannot register a test" 69 | "Keyset failure" 70 | (register-test "somePublicKeyForPrinter0" "some manufacturer 0" "some model 0" [{"acct-name": "key-one", "ks-name": "ks1"},{"acct-name": "key-two", "ks-name": "ks2"}])) 71 | 72 | ; Must sign with a registered entity key 73 | (env-keys ["somePublicKeyForPrinter0"]) 74 | 75 | ; (env-data { 'ks: ["somePublicKeyForTest0"] }) 76 | (env-data { "ks1": ["key-one"], "ks2": ["key-two"]}) 77 | (expect 78 | "priting entity can add test" 79 | "2 tests registered on chain by printer=somePublicKeyForPrinter0" 80 | (register-test "somePublicKeyForPrinter0" "some manufacturer 0" "some model 0" [{"acct-name": "key-one", "ks-name": "ks1"},{"acct-name": "key-two", "ks-name": "ks2"}])) 81 | 82 | (expect 83 | "coin account created for test" 84 | "key-one" 85 | (at "account" (coin.details "key-one"))) 86 | 87 | (env-data { 'ks1: ["pub-key1"], 'ks2: ["pub-key2"] }) 88 | (register-test "somePublicKeyForPrinter0" "some manufacturer 0" "some model 0" [{"acct-name": "pub-key1", "ks-name": "ks1"},{"acct-name": "pub-key2", "ks-name": "ks2"}]) 89 | (commit-tx) 90 | 91 | ;; =============== 92 | ;; TEST: adminitering a test 93 | ;; =============== 94 | (begin-tx) 95 | (env-keys []) 96 | (use user.covid) 97 | 98 | (expect-failure 99 | "only registered tests can be adminitered" 100 | "row not found: randomPubKey" 101 | (administer-test "randomPubKey" "0-10" "male" "USA" "11249" "someRandomHash")) 102 | 103 | (env-keys ["randomPublicKey"]) 104 | (expect-failure 105 | "test must be signed with corresponding keys in coin contract" 106 | "Keyset failure" 107 | (administer-test "pub-key1" "0-10" "male" "USA" "11249" "someRandomHash")) 108 | 109 | (env-chain-data { "chain-id": "0", "block-height": 20 }) 110 | (env-keys ["pub-key1"]) 111 | (expect-failure 112 | "test must be adminitered before it is ended" 113 | "this test has not yet been administered" 114 | (end-test "pub-key1" "negative")) 115 | 116 | (env-chain-data { "chain-id": "0", "block-height": 20 }) 117 | (env-keys ["pub-key1"]) 118 | (expect 119 | "test must be signed with corresponding keys in coin contract" 120 | "Test with public key=pub-key1 administered to patient=someRandomHash" 121 | (administer-test "pub-key1" "0-10" "male" "USA" "11249" "someRandomHash")) 122 | 123 | (env-chain-data { "chain-id": "0", "block-height": 25 }) 124 | (env-keys ["pub-key1"]) 125 | (expect-failure 126 | "test cannot be administered twice" 127 | "this test has already been administered" 128 | (administer-test "pub-key1" "0-10" "male" "USA" "11249" "someRandomHash")) 129 | 130 | (commit-tx) 131 | 132 | 133 | ;; =============== 134 | ;; TEST: ending a test 135 | ;; =============== 136 | (begin-tx) 137 | (env-keys []) 138 | (use user.covid) 139 | 140 | (expect-failure 141 | "only registered tests can be ended" 142 | "row not found: randomPubKey" 143 | (end-test "randomPubKey" "negative")) 144 | 145 | (env-keys ["randomPublicKey"]) 146 | (expect-failure 147 | "test must be signed with corresponding keys in coin contract" 148 | "Keyset failure" 149 | (end-test "pub-key1" "negative")) 150 | 151 | (env-chain-data { "chain-id": "0", "block-height": 20 }) 152 | (env-keys ["pub-key1"]) 153 | (expect 154 | "test must be signed with corresponding keys in coin contract" 155 | "Test with public key=pub-key1 ended with result=negative" 156 | (end-test "pub-key1" "negative")) 157 | 158 | (env-chain-data { "chain-id": "0", "block-height": 25 }) 159 | (env-keys ["pub-key1"]) 160 | (expect-failure 161 | "test cannot be ended twice" 162 | "this test has already been ended" 163 | (end-test "pub-key1" "negative")) 164 | 165 | (commit-tx) 166 | -------------------------------------------------------------------------------- /pact/dapp-contracts/covid/dashboard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kadena-io/developer-scripts/4285732c565098469cd0e079b9df1cef87ae6ec2/pact/dapp-contracts/covid/dashboard.png -------------------------------------------------------------------------------- /pact/dapp-contracts/crowdfund/README.md: -------------------------------------------------------------------------------- 1 | # Crowdfunding App 2 | 3 | This contract is designed to create a decentralized crowdfunding system using 2-step escrow design. 4 | 5 | Learn about Pact's multi-stage sequential transactions, [pacts](https://pact-language.readthedocs.io/en/latest/pact-reference.html#asynchronous-transaction-automation-with-pacts), and [pact-guards](https://pact-language.readthedocs.io/en/latest/pact-reference.html#pact-guards) to leverage its use. 6 | 7 | ## Governance 8 | The contract is governed by a keyset, "admin-keyset" 9 | 10 | Learn more about Pact keysets [here](https://pact-language.readthedocs.io/en/latest/pact-reference.html#keysets-and-authorization) 11 | 12 | ## Tables 13 | The contract contains a `campaigns-table` to store each campaign details, and a `fund-table` to keep track of fund invested to a campaign. 14 | - **campaigns-table** : `title` `description` `target-raise` `current-raise` `start-date` `target-date` `ownerAccount` `guard` `status` 15 | - **fund-table** : `campaign-title` `fundOwner` `pact-id` `escrow` `status` 16 | 17 | Learn more about Pact tables [here](https://pact-language.readthedocs.io/en/latest/pact-reference.html#deftable) 18 | 19 | 20 | ## Functions 21 | 22 | ### create-campaign 23 | - Function for any user to create a new fundraising campaign: `title` `description` `ownerAccount` `target-raise` `start-date` `target-date` 24 | - Inserts the campaign into `campaigns-table` 25 | ``` 26 | (create-campaign 27 | "project1" "DESCRIPTION" "crowd-acct" 800.0 28 | (time "2019-08-21T12:00:00Z") (time "2019-08-26T12:00:00Z")) 29 | ``` 30 | 31 | ### fund-campaign 32 | - Function to fund a campaign in the table: `from` `title` `amount` `escrow` 33 | - A multi-step, ["pacts"](https://pact-language.readthedocs.io/en/latest/pact-reference.html#asynchronous-transaction-automation-with-pacts) function to use escrow account in funding the campaign. Learn about *pacts* [here](https://pact-language.readthedocs.io/en/latest/pact-reference.html#asynchronous-transaction-automation-with-pacts). 34 | - Step 0: Secure amount from the user's account into designated escrow account. 35 | - Step 0 - rollback: Refund the amount from escrow account back to user's account. 36 | - Only executed if the user signs it, campaign is canceled, or failed to meet its target raise after the target date. 37 | - Step 1 : Transfer escrow account's funds into the campaign holder's account. 38 | - Only executable if campaign meets its target and succeeds.. 39 | - Inserts the fund information into `fund-table`. 40 | 41 | ``` 42 | (fund-campaign "kate-acct" "project1" 400.0 "escrow-0") 43 | ``` 44 | 45 | 46 | ### cancel-campaign 47 | - Function for a campaign holder to cancel a campaign before target date: `title` 48 | - Updates the status of a campaign in the `campaigns-table` to `CANCELLED` 49 | - Lets the funds in the `funds-table` tied to the campaign to be refundable.(Step 0-rollback of [`fund-campaign`](#fund-campaign)) 50 | ``` 51 | (cancel-campaign 'project1) 52 | ``` 53 | 54 | ### fail-campaign 55 | - Function to fail a campaign when it does not meet its target raise by target date: `title` 56 | - Updates the status of a campaign in the `campaigns-table` to `FAILED` 57 | - Can be executed by any entity if conditions meet. 58 | - Lets the funds in the `funds-table` tied to the campaign to be refundable. (Step 0-rollback of [`fund-campaign`](#fund-campaign)) 59 | 60 | ``` 61 | (fail-campaign 'project1) 62 | ``` 63 | 64 | ### succeed-campaign 65 | - Function to succeed a campaign when it meets its target raise by target date: `title` 66 | - Updates the status of a campaign in the `campaigns-table` to `SUCCEEDED` 67 | - Can be executed by any entity if conditions meet. 68 | - Lets the funds in the `funds-table` tied to the campaign to be transferred to campaign holder's account. (Step 1 of [`fund-campaign`](#fund-campaign)) 69 | 70 | ``` 71 | (fail-campaign 'project1) 72 | ``` 73 | -------------------------------------------------------------------------------- /pact/dapp-contracts/crowdfund/crowdfund-pacts.pact: -------------------------------------------------------------------------------- 1 | (namespace "user") 2 | (define-keyset 'admin-keyset (read-keyset 'admin-keyset)) 3 | 4 | (module crowdfund-campaign 'admin-keyset 5 | (use coin) 6 | ;define campaign schema 7 | (defschema campaign 8 | title:string 9 | description:string 10 | target-raise:decimal 11 | current-raise:decimal 12 | start-date:time 13 | target-date:time 14 | ownerAccount:string 15 | guard:guard 16 | status:integer 17 | ) 18 | 19 | (defschema fund 20 | campaign-title:string 21 | fundOwner:string 22 | pact-id:string 23 | escrow:string 24 | status:integer 25 | ) 26 | 27 | (deftable campaigns-table:{campaign}) 28 | (deftable fund-table:{fund}) 29 | 30 | (defconst CREATED 0) 31 | (defconst CANCELLED 1) 32 | (defconst SUCCEEDED 2) 33 | (defconst FAILED 3) 34 | 35 | (defun crowdfund-guard:guard () (create-module-guard 'crowdfund-guard)) 36 | 37 | (defcap ACCT_GUARD (account) 38 | (enforce-guard (at 'guard (details account)))) 39 | 40 | (defcap CAMPAIGN_GUARD (title) 41 | (with-read campaigns-table title { 42 | "guard":=guard 43 | } 44 | (enforce-guard guard)) 45 | ) 46 | 47 | (defcap ROLLBACK (title from:string) 48 | (with-read campaigns-table title { 49 | "target-date":=target-date, 50 | "start-date":=start-date, 51 | "status":= status 52 | } 53 | (let ((from-guard (at 'guard (details from)) 54 | )) 55 | (enforce-one "refund guard failure or campaign already succeeded" [ 56 | (enforce (enforce-refund target-date start-date status from-guard) 57 | "Campaign is not open or guards don't match") 58 | (enforce (= status CANCELLED) "Campaign has cancelled") 59 | (enforce (= status FAILED) "Campaign has failed") 60 | ]) 61 | ))) 62 | 63 | (defun enforce-refund:bool (target-date:time start-date:time status:integer issuer-guard:guard) 64 | (enforce (!= status CANCELLED) "CAMPAIGN HAS BEEN CANCELLED") 65 | (enforce (!= status FAILED) "Campaign has failed") 66 | (enforce (!= status SUCCEEDED) "Campaign has failed") 67 | (enforce (< (curr-time) target-date) "CAMPAIGN HAS ENDED") 68 | (enforce (>= (curr-time) start-date) "CAMPAIGN HAS NOT STARTED") 69 | (enforce-guard issuer-guard) 70 | ) 71 | 72 | (defcap CANCEL:bool (title) 73 | (with-read campaigns-table title{ 74 | "status":=status 75 | } 76 | (enforce (= status CANCELLED) "NOT CANCELLED"))) 77 | 78 | (defcap OPEN:bool (title) 79 | (with-read campaigns-table title{ 80 | "target-date":=target-date, 81 | "start-date":=start-date, 82 | "status":=status 83 | } 84 | (enforce (!= status CANCELLED) "CAMPAIGN HAS BEEN CANCELLED") 85 | (enforce (< (curr-time) target-date) "CAMPAIGN HAS ENDED") 86 | (enforce (>= (curr-time) start-date) "CAMPAIGN HAS NOT STARTED"))) 87 | 88 | (defcap SUCCESS:bool (title) 89 | (with-read campaigns-table title{ 90 | "target-raise":=target-raise, 91 | "current-raise":=current-raise, 92 | "target-date":=target-date, 93 | "status":=status 94 | } 95 | (enforce (>= (curr-time) target-date) "CAMPAIGN HAS NOT ENDED") 96 | (enforce (>= current-raise target-raise) "CAMPAIGN HAS NOT RAISED ENOUGH") 97 | (enforce (!= status CANCELLED) "CAMPAIGN HAS BEEN CANCELLED") 98 | )) 99 | 100 | (defcap FAIL:bool (title) 101 | (with-read campaigns-table title{ 102 | "target-raise":=target-raise, 103 | "target-date":=target-date, 104 | "current-raise":=current-raise, 105 | "status":=status 106 | } 107 | (enforce (!= status CANCELLED) "CAMPAIGN HAS BEEN CANCELLED") 108 | (enforce (>= (curr-time) target-date) "CAMPAIGN HAS NOT ENDED") 109 | (enforce (< current-raise target-raise) "CAMPAIGN HAS SUCCEEDED"))) 110 | 111 | 112 | (defcap REFUND () true) 113 | (defcap RAISE () true) 114 | 115 | (defun create-campaign ( 116 | title:string description:string 117 | ownerAccount:string target-raise:decimal 118 | start-date:time target-date:time) 119 | "Adds a campaign to campaign table" 120 | (enforce (< (curr-time) start-date) "Start Date shouldn't be in the past") 121 | (enforce (< start-date target-date) "Start Date should be before target-date") 122 | (enforce (< 0.0 target-raise) "Target raise is not positive number") 123 | 124 | (with-capability (ACCT_GUARD ownerAccount) 125 | (insert campaigns-table title { 126 | "title": title, 127 | "description": description, 128 | "target-raise":target-raise, 129 | "current-raise": 0.0, 130 | "start-date":start-date, 131 | "target-date":target-date, 132 | "ownerAccount": ownerAccount, 133 | "guard": (at 'guard (details ownerAccount)), 134 | "status": CREATED 135 | }))) 136 | 137 | (defun read-campaigns:list () 138 | "Read all campaigns in campaign table" 139 | (select campaigns-table 140 | ['title 'description 'ownerAccount 'start-date 'target-date 'current-raise 'target-raise 'status] 141 | (constantly true))) 142 | 143 | (defun cancel-campaign (title) 144 | (with-capability (CAMPAIGN_GUARD title) 145 | (update campaigns-table title { 146 | "status": CANCELLED 147 | })) 148 | ;;Rollback all -get all pacts and rollback 149 | ) 150 | 151 | (defun succeed-campaign (title) 152 | (with-capability (SUCCESS title) 153 | (update campaigns-table title { 154 | "status": SUCCEEDED 155 | })) 156 | ;;resolve pacts - get all pacts and resolve 157 | ) 158 | 159 | (defun fail-campaign (title) 160 | (with-capability (FAIL title) 161 | (update campaigns-table title { 162 | "status": FAILED 163 | })) 164 | ;;Rollback all - get all pacts and rollback 165 | ) 166 | 167 | (defun create-fund (title funder escrow) 168 | (insert fund-table (pact-id) { 169 | "campaign-title":title, 170 | "escrow": escrow, 171 | "fundOwner":funder, 172 | "pact-id":(pact-id), 173 | "status":CREATED 174 | })) 175 | 176 | (defun cancel-fund (title funder escrow) 177 | (require-capability (ROLLBACK title funder)) 178 | (update fund-table (pact-id) { 179 | "status":CANCELLED 180 | })) 181 | 182 | (defun fetch-pacts:list (title:string) 183 | (select fund-table (where 'campaign-title (= title)))) 184 | 185 | (defun raise-campaign (title amount) 186 | (require-capability (RAISE)) 187 | (with-read campaigns-table title { 188 | "current-raise":= current-raise 189 | } 190 | (update campaigns-table title { 191 | "current-raise": (+ current-raise amount) 192 | }))) 193 | 194 | (defun refund-campaign (title amount) 195 | (require-capability (REFUND)) 196 | (with-read campaigns-table title { 197 | "current-raise":= current-raise 198 | } 199 | (update campaigns-table title { 200 | "current-raise": (- current-raise amount) 201 | }))) 202 | 203 | (defpact fund-campaign (from title amount escrow) 204 | 205 | (step-with-rollback 206 | ;;initiate 207 | (with-capability (ACCT_GUARD from) 208 | (with-capability (OPEN title) 209 | (with-capability (RAISE) 210 | ;; use pact guard 211 | (transfer-create from escrow (create-pact-guard escrow) amount) 212 | (create-fund title from escrow) 213 | (raise-campaign title amount) 214 | ))) 215 | ;;rollback 216 | (with-capability (REFUND) 217 | (with-capability (ROLLBACK title from) 218 | (transfer escrow from amount) 219 | (cancel-fund title from escrow) 220 | (refund-campaign title amount))) 221 | ) 222 | ;;Executes when the campaign meets the goal 223 | (step 224 | (with-capability (SUCCESS title) 225 | (with-read campaigns-table title {"ownerAccount":= owner } 226 | (transfer escrow owner amount))))) 227 | 228 | (defun curr-time:time () 229 | @doc "Returns current chain's block-time in time type" 230 | (at 'block-time (chain-data))) 231 | ) 232 | 233 | (create-table campaigns-table) 234 | (create-table fund-table) 235 | -------------------------------------------------------------------------------- /pact/dapp-contracts/crowdfund/crowdfund.repl: -------------------------------------------------------------------------------- 1 | (begin-tx) 2 | 3 | ;;Load Namespace Contract 4 | (env-data 5 | { 'ns-admin-keyset: ["admin"] 6 | , 'ns-operate-keyset: ["operate"] 7 | , 'ns-genesis-keyset: { "keys": [], "pred": "="} }) 8 | (load "../../dapp-contracts/namespaces/ns.pact") 9 | 10 | ;;Load Coin Contract 11 | (load "../../dapp-contracts/coin-contract/fungible-v2.pact") 12 | (load "../../dapp-contracts/coin-contract/coin.pact") 13 | (create-table coin.coin-table) 14 | 15 | (commit-tx) 16 | 17 | (begin-tx) 18 | (env-keys ["admin" "kate" "bob" "crowd" "s"]) 19 | (env-data { "admin-keyset": ["admin"],"crowd-keyset": ["crowd"], "kate-keyset": ["kate"], "bob-keyset": ["bob"] }) 20 | (load "crowdfund-pacts.pact") 21 | (commit-tx) 22 | (typecheck "user.crowdfund-campaign") 23 | (verify "user.crowdfund-campaign") 24 | 25 | ;;Set block time to (time "2019-05-08T17:03:20Z") 26 | (begin-tx) 27 | (env-chain-data {"block-time": (time "2019-05-08T17:03:20Z")}) 28 | (use user.crowdfund-campaign) 29 | (use coin) 30 | 31 | (test-capability (COINBASE)) 32 | (coinbase "kate-acct" (read-keyset "kate-keyset") 500.0) 33 | (coinbase "bob-acct" (read-keyset "bob-keyset") 500.0) 34 | 35 | (create-account "crowd-acct" (read-keyset "crowd-keyset")) 36 | (create-campaign "project1" "DESCRIPTION" "crowd-acct" 800.0 (time "2019-08-21T12:00:00Z") (time "2019-08-26T12:00:00Z")) 37 | 38 | (read-campaigns) 39 | (commit-tx) 40 | 41 | ;;Initiate Funding 42 | (begin-tx) 43 | (use user.crowdfund-campaign) 44 | (use coin) 45 | 46 | (env-chain-data {"block-time": (time "2019-08-22T12:00:00Z")}) 47 | (env-sigs [{ 48 | 'key: "kate", 49 | 'caps: [ 50 | (coin.TRANSFER "kate-acct" "escrow-0" 400.0) 51 | (ACCT_GUARD "kate-acct")]}]) 52 | 53 | ;;Execute step 0 of kate-acct's "fund-campaign" 54 | (fund-campaign "kate-acct" "project1" 400.0 "escrow-0") 55 | 56 | (expect "100.0" 100.0 (coin.get-balance 'kate-acct)) 57 | (expect "400.0" 400.0 (coin.get-balance 'escrow-0)) 58 | (commit-tx) 59 | 60 | (begin-tx) 61 | (use user.crowdfund-campaign) 62 | 63 | (env-hash (hash "hello")) 64 | 65 | (env-sigs [{ 66 | 'key: "bob", 67 | 'caps: [ 68 | (coin.TRANSFER "bob-acct" "escrow-1" 500.0) 69 | (ACCT_GUARD "bob-acct")]}]) 70 | 71 | ;;Execute step 0 of bob-acct's "fund-campaign" 72 | (fund-campaign "bob-acct" "project1" 500.0 "escrow-1") 73 | 74 | (expect "0.0" 0.0 (coin.get-balance 'bob-acct)) 75 | (expect "500.0" 500.0 (coin.get-balance 'escrow-1)) 76 | 77 | (commit-tx) 78 | 79 | ;;First Scenario - refund executed by signing with kate and bob's keyset 80 | (begin-tx) 81 | (env-sigs [ 82 | { 83 | 'key: "escrow-0", 84 | 'caps: [ 85 | (coin.TRANSFER "escrow-0" "kate-acct" 400.0)]} 86 | { 87 | 'key: "kate", 88 | 'caps: []} 89 | { 90 | 'key: "escrow-1", 91 | 'caps: [ 92 | (coin.TRANSFER "escrow-1" "bob-acct" 500.0)]} 93 | { 94 | 'key: "bob", 95 | 'caps: []} 96 | ]) 97 | (use user.crowdfund-campaign) 98 | 99 | ;;Kate's fund 400.0 is sent to escrow-0 , Bob's fund 500.0 is sent to escrow-1 100 | (expect "100.0" 100.0 (coin.get-balance 'kate-acct)) 101 | (expect "0.0" 0.0 (coin.get-balance 'bob-acct)) 102 | (expect "400.0" 400.0 (coin.get-balance 'escrow-0)) 103 | (expect "500.0" 500.0 (coin.get-balance 'escrow-1)) 104 | 105 | ;;Rollback kate's fund-campaign 106 | (expect "Kate's refund executed" 107 | "Write succeeded" 108 | (continue-pact 0 true "DldRwCblQ7Loqy6wYJnaodHl30d3j3eH-qtFzfEv46g")) 109 | 110 | ;;Rollback Bob's fund-campaign 111 | (expect "Bob's refund executed" 112 | "Write succeeded" 113 | (continue-pact 0 true "Mk3PAn3UowqTLEQfNlol6GsXPe-kuOWJSCU0cbgbcs8")) 114 | 115 | ;;Kate and Bob's funds are refunded 116 | (expect "0.0" 0.0 (coin.get-balance 'escrow-0)) 117 | (expect "0.0" 0.0 (coin.get-balance 'escrow-1)) 118 | (expect "500.0" 500.0 (coin.get-balance 'kate-acct)) 119 | (expect "500.0" 500.0 (coin.get-balance 'bob-acct)) 120 | 121 | (rollback-tx) 122 | 123 | ;;Second Scenario - campaign canceled by the campaign owner, so canacel is executed 124 | 125 | (begin-tx) 126 | (use user.crowdfund-campaign) 127 | (env-chain-data {"block-time": (time "2019-08-24T12:00:00Z")}) 128 | (env-sigs [ 129 | { 130 | 'key: "crowd", 131 | 'caps: [ 132 | (CAMPAIGN_GUARD 'project1)]} 133 | { 134 | 'key: "escrow-0", 135 | 'caps: [ 136 | (coin.TRANSFER "escrow-0" "kate-acct" 400.0)]} 137 | { 138 | 'key: "escrow-1", 139 | 'caps: [ 140 | (coin.TRANSFER "escrow-1" "bob-acct" 500.0)]} 141 | ]) 142 | 143 | (cancel-campaign 'project1) 144 | ;;Rollback kate's fund-campaign 145 | (expect "Kate's refund executed" 146 | "Write succeeded" 147 | (continue-pact 0 true "DldRwCblQ7Loqy6wYJnaodHl30d3j3eH-qtFzfEv46g")) 148 | 149 | ;;Rollback Bob's fund-campaign 150 | (expect "Bob's refund executed" 151 | "Write succeeded" 152 | (continue-pact 0 true "Mk3PAn3UowqTLEQfNlol6GsXPe-kuOWJSCU0cbgbcs8")) 153 | 154 | (rollback-tx) 155 | 156 | ;;Third Scenario - campaign succeeds 157 | (begin-tx) 158 | (env-chain-data {"block-time": (time "2019-08-22T12:00:00Z")}) 159 | 160 | (env-sigs [ 161 | { 162 | 'key: "escrow-0", 163 | 'caps: [ 164 | (coin.TRANSFER "escrow-0" "crowd-acct" 400.0)]} 165 | { 166 | 'key: "escrow-1", 167 | 'caps: [ 168 | (coin.TRANSFER "escrow-1" "crowd-acct" 500.0)]} 169 | ]) 170 | (use user.crowdfund-campaign) 171 | (env-hash (hash "hellddo")) 172 | (env-chain-data {"block-time": (time "2019-08-27T12:00:00Z")}) 173 | 174 | (succeed-campaign 'project1) 175 | 176 | ;;Rollback Fails 177 | (expect-failure "Can't rollback once campaign succeeds" 178 | (continue-pact 0 true "DldRwCblQ7Loqy6wYJnaodHl30d3j3eH-qtFzfEv46g")) 179 | 180 | (expect "Kate's Fund transferred to campaign owner" 181 | "Write succeeded" 182 | (continue-pact 1 false "DldRwCblQ7Loqy6wYJnaodHl30d3j3eH-qtFzfEv46g")) 183 | 184 | (expect "Bob's Fund transferred to campaign owner" 185 | "Write succeeded" 186 | (continue-pact 1 false "Mk3PAn3UowqTLEQfNlol6GsXPe-kuOWJSCU0cbgbcs8")) 187 | 188 | ;;Funds in all accounts 189 | (expect "money went through after campaign success" 900.0 (coin.get-balance 'crowd-acct)) 190 | (expect "0.0" 0.0 (coin.get-balance 'escrow-0)) 191 | (expect "0.0" 0.0 (coin.get-balance 'escrow-1)) 192 | (expect "100.0" 100.0 (coin.get-balance 'kate-acct)) 193 | (expect "0.0" 0.0 (coin.get-balance 'bob-acct)) 194 | 195 | (rollback-tx) 196 | -------------------------------------------------------------------------------- /pact/dapp-contracts/faucet/README.md: -------------------------------------------------------------------------------- 1 | # Coin Faucet 2 | 3 | ![faucet](testnet-faucet.png) 4 | 5 | This contract is designed to provide free KDA tokens Kadena testnet. 6 | - The tokens are held in coin account, `coin-faucet`. This account is guarded by a module guard, `faucet-guard`. Read about module guards [here](https://pact-language.readthedocs.io/en/latest/pact-reference.html#module-guards). 7 | - Each of the receiver accounts have time stamped in the contract. 8 | - The maximum amount of coins to request at once is 20 KDA. 9 | - If receiver has received coins in less than 30 minutes, then coin request is aborted. 10 | - There are two types of requests. When one has a coin account, they should `request-coin`. However, if no account exists on network, `create-and-request-coin` will create an account and send the tokens to the account. The functions each use `coin.transfer` and `coin.transfer-create` to do this. 11 | 12 | ## Governance 13 | The contract is governed by a capability, "GOVERNANCE". The capability is guarded by the guard of the coin account, `contract-admins`. 14 | 15 | Learn more about Pact module governance [here](https://pact-language.readthedocs.io/en/stable/pact-reference.html#generalized-module-governance). 16 | 17 | ## Tables 18 | The contract contains a history table to track the receiver account's activity with the faucet account. 19 | - **history-table** : `total-coins-earned` `total-coins-returned` `last-request-time` 20 | 21 | Learn more about Pact tables [here](https://pact-language.readthedocs.io/en/latest/pact-reference.html#deftable) 22 | 23 | ## Functions 24 | 25 | ### request-coin 26 | - Transfers KDA tokens from faucet account to receiver: `address` `amount` 27 | - Enforces that the requested amount is less than MAX_COIN_PER_REQUEST, which is 20.0 in this module 28 | - Reads the history table and enforce that the request's account had not requested in 30 minutes 29 | - `coin.transfer` `amount` from `FAUCET_ACCOUNT` to `address`. 30 | - Updates the receiver's history table. 31 | ``` 32 | (request-coin "test-account" 10.0) 33 | ``` 34 | 35 | ### create-and-request-coin 36 | - Create a new receiver account and transfer KDA token from faucet account to receiver: `address` `address-guard` `amount` 37 | - Enforces that the requested amount is less than MAX_COIN_PER_REQUEST, which is 20.0 in this module 38 | - `coin.transfer-create` `amount` from `FAUCET_ACCOUNT` to `address` 39 | - Updates the receiver's history table. 40 | ``` 41 | (create-and-request-coin "test-account" (read-keyset "test-keyset") 10.0) 42 | ``` 43 | 44 | ### return-coin 45 | - transfers KDA token from receiver account to faucet account: `address` `amount` 46 | ``` 47 | (return-coin "test-account" 10.0) 48 | ``` 49 | 50 | Learn more about Pact functions [here](https://pact-language.readthedocs.io/en/latest/pact-reference.html#defun) 51 | 52 | ## Demo 53 | 54 | The Contract is deployed on Kadena Testnet, and is usable [here](https://faucet.testnet.chainweb.com/) 55 | -------------------------------------------------------------------------------- /pact/dapp-contracts/faucet/testnet-faucet.pact: -------------------------------------------------------------------------------- 1 | (namespace "user") 2 | (module coin-faucet GOVERNANCE 3 | 4 | "'coin-faucet' represents Kadena's Coin Faucet Contract." 5 | 6 | ;; TODO - use hashed import 7 | (use coin) 8 | 9 | ; -------------------------------------------------------------------------- 10 | ; Governance 11 | ; -------------------------------------------------------------------------- 12 | 13 | (defcap GOVERNANCE () 14 | (enforce-guard (at 'guard (details 'contract-admins ))) 15 | true) 16 | ; -------------------------------------------------------------------------- 17 | ; Schemas and Tables 18 | ; -------------------------------------------------------------------------- 19 | 20 | (defschema history 21 | @doc "Table to record the behavior of addresses. Last transaction time, \ 22 | \ total coins earned, and total coins returned are inserted or updated at \ 23 | \ transaction. " 24 | total-coins-earned:decimal 25 | total-coins-returned:decimal 26 | last-request-time:time 27 | ) 28 | 29 | (deftable history-table: {history}) 30 | 31 | ; -------------------------------------------------------------------------- 32 | ; Constants 33 | ; -------------------------------------------------------------------------- 34 | 35 | (defconst FAUCET_ACCOUNT:string 'coin-faucet ) 36 | (defconst MAX_COIN_PER_REQUEST:decimal 20.0) 37 | (defconst WAIT_TIME_PER_REQUEST 1800.0) 38 | (defconst EPOCH (time "1970-01-01T00:00:00Z")) 39 | 40 | ; -------------------------------------------------------------------------- 41 | ; Coin Faucet Contract 42 | ; -------------------------------------------------------------------------- 43 | 44 | (defun faucet-guard:guard () (create-module-guard 'faucet-admin ) ) 45 | 46 | (defun request-coin:string (address:string amount:decimal) 47 | 48 | (enforce (<= amount MAX_COIN_PER_REQUEST) 49 | "Has reached maximum coin amount per request") 50 | 51 | (transfer FAUCET_ACCOUNT address amount) 52 | 53 | (with-default-read history-table address 54 | { "total-coins-earned": 0.0, 55 | "total-coins-returned": 0.0, 56 | "last-request-time": EPOCH 57 | } 58 | { "total-coins-earned":= total-coins-earned, 59 | "total-coins-returned":= total-coins-returned, 60 | "last-request-time":= last-request-time 61 | } 62 | 63 | (enforce (>= (diff-time (curr-time) last-request-time) WAIT_TIME_PER_REQUEST) 64 | "Coin can be requested every 30 minutes") 65 | 66 | (let (( total-coins (+ amount total-coins-earned))) 67 | 68 | (write history-table address { 69 | "total-coins-earned": total-coins, 70 | "total-coins-returned": total-coins-returned, 71 | "last-request-time": (curr-time) })))) 72 | 73 | (defun create-and-request-coin:string (address:string address-guard:guard amount:decimal) 74 | @doc "Transfers AMOUNT of coins up to MAX_COIN_PER_REQUEST from the faucet \ 75 | \ account to the requester account at ADDRESS. Inserts or updates the \ 76 | \ transaction of the account at ADDRESS in history-table. Limits the number \ 77 | \ of coin requests by time, WAIT_TIME_PER_REQUEST " 78 | @model [(property (<= amount MAX_COIN_PER_REQUEST))] 79 | 80 | (enforce (<= amount MAX_COIN_PER_REQUEST) 81 | "Has reached maximum coin amount per request") 82 | 83 | (transfer-create FAUCET_ACCOUNT address address-guard amount) 84 | (insert history-table address { 85 | "total-coins-earned": amount, 86 | "total-coins-returned": 0.0, 87 | "last-request-time": (curr-time) })) 88 | 89 | (defun return-coin:string (address:string amount:decimal) 90 | @doc "Returns the AMOUNT of coin from account at ADDRESS back to the faucet \ 91 | \ account after use. Updates the transaction of the account at ADDRESS in \ 92 | \ history-table keep track of behavior. " 93 | @model [(property (> amount 0.0))] 94 | 95 | (with-read history-table address 96 | {"total-coins-returned":= coins-returned} 97 | (transfer address FAUCET_ACCOUNT amount) 98 | (update history-table address 99 | {"total-coins-returned": (+ amount coins-returned)}))) 100 | 101 | (defun read-history:object{history} (address:string) 102 | @doc "Returns history of the account at ADDRESS" 103 | (read history-table address)) 104 | 105 | (defun curr-time () 106 | (at 'block-time (chain-data))) 107 | ) 108 | 109 | (create-table history-table) 110 | (coin.create-account FAUCET_ACCOUNT (faucet-guard)) 111 | -------------------------------------------------------------------------------- /pact/dapp-contracts/faucet/testnet-faucet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kadena-io/developer-scripts/4285732c565098469cd0e079b9df1cef87ae6ec2/pact/dapp-contracts/faucet/testnet-faucet.png -------------------------------------------------------------------------------- /pact/dapp-contracts/faucet/testnet-faucet.repl: -------------------------------------------------------------------------------- 1 | (begin-tx) 2 | 3 | ;;Load Namespace Contract 4 | (env-data 5 | { 'ns-admin-keyset: ["admin"] 6 | , 'ns-operate-keyset: ["operate"] 7 | , 'ns-genesis-keyset: { "keys": [], "pred": "="} }) 8 | (load "../../dapp-contracts/namespaces/ns.pact") 9 | 10 | ;;Load Coin Contract 11 | (load "../../dapp-contracts/coin-contract/fungible-v2.pact") 12 | (load "../../dapp-contracts/coin-contract/coin.pact") 13 | (create-table coin.coin-table) 14 | 15 | (env-data { 16 | "contract-admins" : ["contract-admins"] 17 | }) 18 | 19 | (test-capability (COINBASE)) 20 | (coinbase 'contract-admins (read-keyset 'contract-admins) 1000000.0) 21 | 22 | (env-keys ["contract-admins"]) 23 | (load "testnet-faucet.pact") 24 | (commit-tx) 25 | 26 | (use user.coin-faucet) 27 | (env-chain-data {"block-time": (time "2019-08-27T12:00:00Z")}) 28 | 29 | ;;coinbase faucet-account 30 | (test-capability (coin.COINBASE)) 31 | (coin.coinbase 'coin-faucet (faucet-guard) 1000.0) 32 | 33 | (env-data { 34 | "test-0-keyset": ["test-0-key"], 35 | "test-1-keyset": ["test-1-key"] 36 | }) 37 | 38 | (env-sigs [ 39 | { 40 | 'key: "0", 41 | 'caps: [ 42 | (coin.TRANSFER "coin-faucet" "test-0" 40.1) 43 | ] 44 | } 45 | { 46 | 'key: "1", 47 | 'caps: [ 48 | (coin.TRANSFER "coin-faucet" "test-1" 40.1) 49 | ] 50 | } 51 | { 52 | 'key: "test-1-key", 53 | 'caps: [ 54 | (coin.TRANSFER "test-1" "coin-faucet" 40.1) 55 | ] 56 | } 57 | ]) 58 | 59 | ;;Test request-coin 60 | (coin.create-account "test-0" (read-keyset "test-0-keyset")) 61 | (expect-failure "exceeded request limit" (request-coin "test-0" 20.1)) 62 | (expect "Write succeeded" "Write succeeded" (request-coin "test-0" 20.0)) 63 | 64 | ;;Test create-and-request-coin 65 | (expect "Write succeeded" "Write succeeded" 66 | (create-and-request-coin "test-1" (read-keyset "test-1-keyset") 20.0)) 67 | 68 | ;;Test return-coin 69 | (expect "Write succeeded" "Write succeeded" 70 | (return-coin "test-1" 10.0)) 71 | (expect-failure "Insufficient funds" 72 | (return-coin "test-1" 10.1)) 73 | -------------------------------------------------------------------------------- /pact/dapp-contracts/hybrid-dapp/README.md: -------------------------------------------------------------------------------- 1 | # Hybrid Dapp 2 | 3 | ![hybrid](hybrid-demo.png) 4 | 5 | This contract demonstrates hybrid platform that allows token conversion between one blockchain and another. 6 | 7 | 8 | ## Governance 9 | The contract is governed by a capability, "GOVERNANCE". The capability is guarded by the guard of the coin account, `contract-admins`. 10 | 11 | Learn more about Module Governance [here](https://pact-language.readthedocs.io/en/stable/pact-reference.html#generalized-module-governance) 12 | 13 | ## Tables 14 | The contract contains a history table to track the receiver account's activity with the faucet account. 15 | - **hybrid-table** : `ht-balance` `coins-in` `coins-out` `ht-account` `req-history` 16 | - **tx-table** : `account` `amount` `status` `time` 17 | 18 | Learn more about Pact tables [here](https://pact-language.readthedocs.io/en/latest/pact-reference.html#deftable) 19 | 20 | ## Functions 21 | 22 | ### buy-ht 23 | - Transfers KDA tokens from user account to admin account: `account` `amount` 24 | - Update the same amount of value from admin's hybrid tokens to user account on `hybrid-table`. 25 | ``` 26 | (buy-ht "test-user" 10.0) 27 | ``` 28 | 29 | ### sell-ht 30 | - Transfers KDA tokens from admin account to user account: `account` `amount` 31 | - Update the same amount of value from user's hybrid tokens to admin account on `hybrid-table`. 32 | ``` 33 | (sell-ht "test-user" 10.0) 34 | ``` 35 | 36 | ### trans-to-priv 37 | - Transfer hybrid token to a private blockchain: `account` `amount` 38 | - Subtract hybrid token on hybrid-table and add the value on the private blockchain. 39 | ``` 40 | (trans-to-priv "test-user" 10.0) 41 | ``` 42 | 43 | Learn more about Pact functions [here](https://pact-language.readthedocs.io/en/latest/pact-reference.html#defun) 44 | 45 | ## Demo 46 | 47 | The Contract is deployed on Kadena Testnet, and is usable [here](https://hybrid.chainweb.com/) 48 | -------------------------------------------------------------------------------- /pact/dapp-contracts/hybrid-dapp/hybrid-demo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kadena-io/developer-scripts/4285732c565098469cd0e079b9df1cef87ae6ec2/pact/dapp-contracts/hybrid-dapp/hybrid-demo.png -------------------------------------------------------------------------------- /pact/dapp-contracts/hybrid-dapp/hybrid-exchange.pact: -------------------------------------------------------------------------------- 1 | (namespace "user") 2 | (module hybrid-exchange GOVERNANCE 3 | 4 | (defcap GOVERNANCE () 5 | (enforce-guard (at 'guard (details 'contract-admins)))) 6 | 7 | (use coin) 8 | 9 | (defschema hybrid-schema 10 | ht-balance:decimal 11 | coins-in:decimal 12 | coins-out:decimal 13 | ht-account:bool 14 | req-history:[string] 15 | ) 16 | 17 | (defschema tx-schema 18 | account:string 19 | amount:decimal 20 | status:string 21 | time:string 22 | ) 23 | 24 | (deftable hybrid-table-2:{hybrid-schema}) 25 | (deftable txs-table-2:{tx-schema}) 26 | 27 | (defconst TX_OPEN:string "open") 28 | (defconst TX_COMPLETE:string "complete") 29 | (defconst TX_REFUNDED:string "rejected") 30 | 31 | ;TOTAL SUPPLY 1 million tokens 32 | (defconst TOTAL_SUPPLY:decimal 1000000.0) 33 | 34 | ;module-guard account 35 | (defconst ADMIN_ACCOUNT:string "hybrid-mg") 36 | (defun ht-guard:guard () (create-module-guard ADMIN_ACCOUNT)) 37 | 38 | 39 | ;need to figure out this admin stuff after 40 | (defcap ADMIN () 41 | "makes sure only contract owner can make important function calls" 42 | (enforce-guard (at 'guard (coin.details 'hybrid-admin))) 43 | ) 44 | 45 | (defcap REGISTERED_USER (account:string) 46 | "makes sure user's guard matches" 47 | ;true 48 | (enforce-guard (at 'guard (coin.details account))) 49 | ) 50 | 51 | 52 | (defun buy-ht (account:string amount:decimal) 53 | @doc "way for user to buy hybrid token with kadena coin \ 54 | \ debits user, credits module-guard" 55 | ;also enforces amount is pos, and user has the funds 56 | ;and that his guard matches the account name 57 | (coin.transfer account ADMIN_ACCOUNT amount) 58 | (with-default-read hybrid-table-2 account 59 | ;{"guard": (at 'guard (coin.account-info account)), 60 | ;placeholder!!!!!!!!!!!!! 61 | ;{"guard": (ht-guard), ;;WARNING!!!! TO BE CHANGED!!!!! 62 | ;change above!!!!!!! 63 | {"ht-balance": 0.0, 64 | "coins-in": 0.0, 65 | "coins-out": 0.0, 66 | "ht-account": false, 67 | "req-history": []} 68 | ;{"guard":= guard-user, 69 | {"ht-balance":= ht-balance-user, 70 | "coins-in":= coins-in-user, 71 | "coins-out":= coins-out-user, 72 | "ht-account":= ht-account, 73 | "req-history" := req-history} 74 | (with-read hybrid-table-2 "admin" 75 | {"coins-in":= coins-in-admin, 76 | "ht-balance":= ht-balance-admin} 77 | 78 | ;make sure we have enough tokens left! 79 | (enforce (<= amount ht-balance-admin) "sorry there are not enough tokens available") 80 | 81 | (write hybrid-table-2 account 82 | ;{"guard": guard-user, 83 | {"ht-balance": (+ ht-balance-user amount), 84 | "coins-in": (+ coins-in-user amount), 85 | "coins-out": coins-out-user, 86 | "ht-account": ht-account, 87 | "req-history": req-history}) 88 | (update hybrid-table-2 "admin" 89 | {"coins-in": (+ coins-in-admin amount), 90 | "ht-balance": (- ht-balance-admin amount)}) 91 | ) 92 | ) 93 | ) 94 | 95 | (defun sell-ht (account:string amount:decimal) 96 | @doc "way for user to sell hybrid token for kadena coin \ 97 | \ debits module guard, credits user" 98 | (with-capability (REGISTERED_USER account) 99 | (with-read hybrid-table-2 account 100 | {"ht-balance":= ht-balance-user, 101 | "coins-out":= coins-out-user} 102 | (enforce (>= ht-balance-user amount) "amount must be less than your current balance") 103 | ;;transfer the money (already checks amount is positive in coin) 104 | (coin.transfer ADMIN_ACCOUNT account amount) 105 | (with-read hybrid-table-2 "admin" 106 | {"ht-balance":= ht-balance-admin, 107 | "coins-out":= coins-out-admin} 108 | (update hybrid-table-2 account 109 | {"ht-balance": (- ht-balance-user amount), 110 | "coins-out": (+ coins-out-user amount)} 111 | ) 112 | (update hybrid-table-2 "admin" 113 | {"ht-balance": (+ ht-balance-admin amount), 114 | "coins-out": (+ coins-out-admin amount)} 115 | ) 116 | )) 117 | ) 118 | ) 119 | 120 | (defun trans-to-priv (account:string amount:decimal) 121 | @doc "user calls this when wanting to transfer hybrid token \ 122 | \ to Kuro private network" 123 | (with-capability (REGISTERED_USER account) 124 | (with-read hybrid-table-2 account 125 | {"ht-balance":= balance-user, 126 | "ht-account":= ht-account} 127 | (enforce (>= balance-user amount) "amount must be less than your current balance") 128 | (enforce (> amount 0.0) "amount must be positive") 129 | (debit-ht-user account amount) 130 | ;;create a uniqueID -> account + timestamp 131 | (let* ((ts (format-time "%Y-%m-%d%H:%M:%S.%v" (at "block-time" (chain-data)))) 132 | (id (format "{}-{}" [account, ts]))) 133 | (insert txs-table-2 id 134 | { "account": account, 135 | "amount": amount, 136 | "status": TX_OPEN, 137 | "time": ts }) 138 | (with-read hybrid-table-2 account 139 | {"req-history":= req-history} 140 | ; (if ht-account 141 | ; (update hybrid-table-2 account 142 | ; {"req-history": (+ req-history [ts])})) 143 | (update hybrid-table-2 account 144 | {"ht-account": true, 145 | "req-history": (+ req-history [ts])})) 146 | ;return some info to user 147 | (format "Confirmation that your request ({}) is being processed..." [id]) 148 | ))) 149 | ) 150 | 151 | (defun credit-ht (account:string amount:decimal) 152 | @doc "ADMIN ONLY: credit user account with amount \ 153 | \ used for incoming tokens FROM Kuro" 154 | (with-capability (ADMIN) 155 | (with-read hybrid-table-2 account 156 | {"ht-balance":= balance-user} 157 | (with-read hybrid-table-2 "admin" 158 | {"ht-balance":= balance-admin} 159 | (update hybrid-table-2 account 160 | {"ht-balance": (+ balance-user amount)}) 161 | (update hybrid-table-2 "admin" 162 | {"ht-balance": (- balance-admin amount)}) 163 | ) 164 | ) 165 | ) 166 | ) 167 | 168 | (defun debit-ht (account:string amount:decimal) 169 | @doc "ADMIN ONLY: debit user account with amount \ 170 | \ of token to be moved TO Kuro" 171 | (with-capability (ADMIN) 172 | (with-read hybrid-table-2 account 173 | {"ht-balance":= balance-user} 174 | (with-read hybrid-table-2 "admin" 175 | {"ht-balance":= balance-admin} 176 | (enforce (>= balance-user amount) "user does not have enough balance") 177 | (update hybrid-table-2 account 178 | {"ht-balance": (- balance-user amount)}) 179 | (update hybrid-table-2 "admin" 180 | {"ht-balance": (+ balance-admin amount)}) 181 | ) 182 | ) 183 | ) 184 | ) 185 | 186 | (defun debit-ht-user (account:string amount:decimal) 187 | @doc "ADMIN ONLY: debit user account with amount \ 188 | \ of token to be moved TO Kuro" 189 | (require-capability (REGISTERED_USER account)) 190 | (with-read hybrid-table-2 account 191 | {"ht-balance":= balance-user} 192 | (with-read hybrid-table-2 "admin" 193 | {"ht-balance":= balance-admin} 194 | (enforce (>= balance-user amount) "user does not have enough balance") 195 | (update hybrid-table-2 account 196 | {"ht-balance": (- balance-user amount)}) 197 | (update hybrid-table-2 "admin" 198 | {"ht-balance": (+ balance-admin amount)}) 199 | ) 200 | ) 201 | ) 202 | 203 | (defun confirm-transfer-to-kuro (id:string) 204 | @doc "ADMIN ONLY: changing status of inter-chain transfers as complete" 205 | (with-capability (ADMIN) 206 | (update txs-table-2 id 207 | {"status": TX_COMPLETE}) 208 | ) 209 | ) 210 | 211 | (defun reject-transfer-to-kuro (id:string) 212 | @doc "ADMIN ONLY: changing status of inter-chain transfers as rejected \ 213 | \ called if debit-ht gets rejected when called from admin \ 214 | \ also refunds the amount to the user" 215 | (with-capability (ADMIN) 216 | (with-read txs-table-2 id 217 | {"amount" := amount, 218 | "account" := account} 219 | (credit-ht account amount) 220 | (update txs-table-2 id 221 | {"status": TX_REFUNDED}) 222 | ) 223 | ) 224 | ) 225 | 226 | ;intended front-end use: (map (hybrid-exchange.get-tx) (hybrid-exchange.get-tx-keys)) 227 | (defun get-tx-keys () 228 | @doc "ADMIN ONLY: see who needs to be credited on either chain \ 229 | \ can potentially encypt users info??" 230 | ;do i need ADMIN cap here, anyone can access this db in theory 231 | (with-capability (ADMIN) 232 | (keys txs-table-2) 233 | ) 234 | ) 235 | (defun get-tx (id:string) 236 | @doc "for user or admin to check status of a tx" 237 | (read txs-table-2 id) 238 | ) 239 | 240 | (defun get-req-history (account:string) 241 | @doc "for user to get all his requested txs" 242 | (with-capability (REGISTERED_USER account) 243 | (at "req-history" (read hybrid-table-2 account)) 244 | ) 245 | ) 246 | 247 | (defun init-admin-account () 248 | @doc "ADMIN ONLY: init admin account --> ONE TIME ONLY FUNCTION" 249 | (with-capability (ADMIN) 250 | (insert hybrid-table-2 "admin" { 251 | ;"guard": (ht-guard), 252 | "ht-balance": TOTAL_SUPPLY, 253 | "coins-in": 0.0, 254 | "coins-out": 0.0, 255 | "ht-account": false, 256 | "req-history": []} 257 | ) 258 | ) 259 | ) 260 | 261 | (defun get-balance (account:string) 262 | (with-read hybrid-table-2 account 263 | {"ht-balance":= balance} 264 | balance 265 | ) 266 | ) 267 | 268 | ) 269 | 270 | (create-table hybrid-table-2) 271 | (create-table txs-table-2) 272 | (init-admin-account) 273 | -------------------------------------------------------------------------------- /pact/dapp-contracts/hybrid-dapp/hybrid-exchange.repl: -------------------------------------------------------------------------------- 1 | 2 | ;-----------------SETUP------------- 3 | (begin-tx) 4 | 5 | ;;Load Namespace Contract 6 | (env-data 7 | { 'ns-admin-keyset: ["admin"] 8 | , 'ns-operate-keyset: ["operate"] 9 | , 'ns-genesis-keyset: { "keys": [], "pred": "="} }) 10 | (load "../../dapp-contracts/namespaces/ns.pact") 11 | 12 | ;;Load Coin Contract 13 | (load "../../dapp-contracts/coin-contract/fungible-v2.pact") 14 | (load "../../dapp-contracts/coin-contract/coin.pact") 15 | (create-table coin.coin-table) 16 | 17 | (env-data { 18 | "contract-admins" : ["contract-admins"], 19 | "hybrid-admin": ["hybrid-admin"] 20 | }) 21 | (test-capability (COINBASE)) 22 | (coinbase 'contract-admins (read-keyset 'contract-admins) 1000000.0) 23 | (coinbase 'hybrid-admin (read-keyset 'hybrid-admin) 1000000.0) 24 | 25 | (commit-tx) 26 | 27 | 28 | (begin-tx) 29 | 30 | (env-data { 31 | "admin": ["admin-key"], 32 | "acct-1": ["acct-1-key"], 33 | "acct-2": ["acct-2-key"], 34 | "acct-3": ["acct-3-key"], 35 | "acct-4": ["acct-4-key"] 36 | }) 37 | (env-keys [ 38 | "admin-key", 39 | "acct-1-key", 40 | "acct-2-key", 41 | "acct-3-key", 42 | "acct-4-key" 43 | ]) 44 | 45 | (define-keyset "admin" (read-keyset "admin")) 46 | 47 | (commit-tx) 48 | 49 | ;-----------------COIN ALLOCATION------------- 50 | (begin-tx) 51 | 52 | (use coin) 53 | 54 | (test-capability (COINBASE)) 55 | (coinbase "admin" (read-keyset "admin") 10000.0) 56 | (coinbase "acct-1" (read-keyset "acct-1") 100.0) 57 | (coinbase "acct-2" (read-keyset "acct-2") 100.0) 58 | (coinbase "acct-3" (read-keyset "acct-3") 100.0) 59 | (coinbase "acct-4" (read-keyset "acct-4") 100.0) 60 | 61 | (env-keys ["contract-admins", "hybrid-admin"]) 62 | 63 | (load "hybrid-exchange.pact") 64 | 65 | (env-sigs [ 66 | { 67 | 'key: "hybrid-admin", 68 | 'caps: [ 69 | (coin.TRANSFER 'hybrid-admin 'hybrid-mg 300.0) 70 | ] 71 | } 72 | ]) 73 | (transfer-create "hybrid-admin" ADMIN_ACCOUNT (ht-guard) 300.0) 74 | 75 | (commit-tx) 76 | 77 | ;-----------------BUY/SELL TEST------------- 78 | (begin-tx) 79 | 80 | (use coin) 81 | (use user.hybrid-exchange) 82 | 83 | (env-sigs [ 84 | { 85 | 'key: "acct-1-key", 86 | 'caps: [ 87 | (coin.TRANSFER 'acct-1 'hybrid-mg 1.0) 88 | (REGISTERED_USER 'acct-1) 89 | ] 90 | } 91 | { 92 | 'key: "", 93 | 'caps: [ 94 | (coin.TRANSFER 'hybrid-mg 'acct-1 1.0) 95 | ] 96 | } 97 | ]) 98 | 99 | 100 | (buy-ht "acct-1" 1.0) 101 | (expect "ht balance 1.0" 1.0 (get-balance "acct-1")) 102 | (expect "coin balance 99.0" 99.0 (coin.get-balance "acct-1")) 103 | 104 | (sell-ht "acct-1" 1.0) 105 | (expect "ht balance 0.0" 0.0 (get-balance "acct-1")) 106 | (expect "coin balance 100.0" 100.0 (coin.get-balance "acct-1")) 107 | 108 | (commit-tx) 109 | 110 | ;-----------------BUY AND REQUEST TEST--------------- 111 | (begin-tx) 112 | 113 | (use coin) 114 | (use user.hybrid-exchange) 115 | (env-sigs [ 116 | { 117 | 'key: "acct-1-key", 118 | 'caps: [ 119 | (coin.TRANSFER 'acct-1 'hybrid-mg 2.0) 120 | (REGISTERED_USER 'acct-1) 121 | ] 122 | } 123 | { 124 | 'key: "", 125 | 'caps: [ 126 | (coin.TRANSFER 'hybrid-mg 'acct-1 1.0) 127 | ] 128 | } 129 | ]) 130 | (env-chain-data {"block-time": (time "2019-08-27T12:00:00Z")}) 131 | (buy-ht "acct-1" 2.0) 132 | (expect "ht balance 2.0" 2.0 (get-balance "acct-1")) 133 | (expect "admin balance TOTAL_SUPPLY - 2" 999998.0 (get-balance "admin")) 134 | (expect "coin balance 98.0" 98.0 (coin.get-balance "acct-1")) 135 | ;300 -> 302 for hybrid-admin -> module guard account that we create 136 | (expect "coin balance admin 300 + 2.0" 302.0 (coin.get-balance "hybrid-mg")) 137 | 138 | (trans-to-priv "acct-1" 2.0) 139 | 140 | (expect "ht balance 0.0" 0.0 (get-balance "acct-1")) 141 | (expect "admin balance back to TOTAL_SUPPLY" 1000000.0 (get-balance "admin")) 142 | 143 | ;produces id of "acct-1-000000" 144 | 145 | (get-tx "acct-1-2019-08-2712:00:00.000000") 146 | (expect "status open" "open" (at "status" (get-tx "acct-1-2019-08-2712:00:00.000000"))) 147 | (expect "amount 2.0" 2.0 (at "amount" (get-tx "acct-1-2019-08-2712:00:00.000000"))) 148 | 149 | (env-keys ["hybrid-admin"]) 150 | 151 | ;try confirm 152 | (confirm-transfer-to-kuro "acct-1-2019-08-2712:00:00.000000") 153 | (expect "complete" "complete" (at "status" (get-tx "acct-1-2019-08-2712:00:00.000000"))) 154 | 155 | ;try reject -> this is called if debit-ht fails 156 | ; which means that user made request, but lowered ht balance 157 | (reject-transfer-to-kuro "acct-1-2019-08-2712:00:00.000000") 158 | (expect "status rejected" "rejected" (at "status" (get-tx "acct-1-2019-08-2712:00:00.000000"))) 159 | 160 | (commit-tx) 161 | -------------------------------------------------------------------------------- /pact/dapp-contracts/loans/README.md: -------------------------------------------------------------------------------- 1 | # Manage Loans 2 | 3 | This contract is designed to manage loans between multiple entities. 4 | The main features of the loans contract is the following: 5 | - Create and initiate a loan 6 | - Assign a loan 7 | - Sell a loan to a different entity 8 | ## Governance 9 | The contract is governed by a keyset, "loans-admin-keyset" 10 | 11 | Learn more about Pact keysets [here](https://pact-language.readthedocs.io/en/latest/pact-reference.html#keysets-and-authorization) 12 | 13 | ## Tables 14 | The contract contains 3 tables with following schema: 15 | - **loans-table**: `loanName` `entityName` `loanAmount` `status` 16 | - **loan-history-table**: `loanId` `buyer` `seller` `amount` 17 | - **loan-inventory-table**: `balance` 18 | 19 | Learn more about Pact tables [here](https://pact-language.readthedocs.io/en/latest/pact-reference.html#deftable) 20 | 21 | ## Functions 22 | 23 | ### create-loan 24 | - Creates a loan with the parameters: `loanId` `loanName` `entityName` `loanAmount` 25 | - Inserts the loan into `loans-table` and `loan-history-table` with status `INITIATED` 26 | ``` 27 | (create-loan "C30231G102-1" "C30231G102" "Exxon Mobil" 100000) 28 | ``` 29 | 30 | ### assign-a-loan 31 | - Assign a loan with parameter : `txid` `loanId` `buyer` `amount` 32 | - Inserts the assignment of the loan into `loan-history-table` and update the balance into the `loan-inventory-table`. 33 | - Updates the status of loan to `assigned` 34 | ``` 35 | (assign-loan "TX_0" "C30231G102-1" "Exxon Mobil" 4000) 36 | ``` 37 | 38 | ### sell-a-loan 39 | - Sell a loan with the parameters: `txid` `loanId` `buyer` `seller` `amount` 40 | - Insert the transaction into `loans-history` and update the balance on `loans-inventory-table` 41 | ``` 42 | (sell-loan "TX_1" "C30231G102-1" "McKesson" "Exxon Mobil" 2000) 43 | ``` 44 | 45 | Learn more about Pact functions [here](https://pact-language.readthedocs.io/en/latest/pact-reference.html#defun) 46 | 47 | 48 | ## Demo 49 | Take a look at detailed description about the project [here](https://pactlang.org/beginner/project-loans) 50 | -------------------------------------------------------------------------------- /pact/dapp-contracts/loans/loans.pact: -------------------------------------------------------------------------------- 1 | (define-keyset 'loans-admin-keyset 2 | (read-keyset "loans-admin-keyset")) 3 | 4 | (module loans 'loans-admin-keyset 5 | 6 | (defschema loan 7 | loanName:string 8 | entityName:string 9 | loanAmount:integer 10 | status:string 11 | ) 12 | 13 | (defschema loan-history 14 | loanId:string 15 | buyer:string 16 | seller:string 17 | amount:integer 18 | ) 19 | 20 | (defschema loan-inventory 21 | balance:integer 22 | ) 23 | 24 | (deftable loans-table:{loan}) 25 | (deftable loan-history-table:{loan-history}) 26 | (deftable loan-inventory-table:{loan-inventory}) 27 | 28 | (defconst INITIATED "initiated") 29 | (defconst ASSIGNED "assigned") 30 | 31 | ;;utility function 32 | (defun inventory-key (loanId:string owner:string) 33 | "Make composite key from OWNER and LoanId" 34 | (format "{}:{}" [loanId owner]) 35 | ) 36 | 37 | (defun create-a-loan (loanId loanName entityName loanAmount) 38 | (insert loans-table loanId { 39 | "loanName":loanName, 40 | "entityName":entityName, 41 | "loanAmount":loanAmount, 42 | "status":INITIATED 43 | }) 44 | (insert loan-inventory-table (inventory-key loanId entityName){ 45 | "balance": loanAmount 46 | })) 47 | 48 | (defun assign-a-loan (txid loanId buyer amount) 49 | ;; read entity name from loans-table 50 | (with-read loans-table loanId { 51 | "entityName":= entityName, 52 | "loanAmount":= issuerBalance 53 | } 54 | ;;insert loan transaction into loan-history table 55 | (insert loan-history-table txid { 56 | "loanId":loanId, 57 | "buyer":buyer, 58 | "seller":entityName, 59 | "amount": amount 60 | }) 61 | ;; insert buyer's loan balance into inventory table 62 | (insert loan-inventory-table (inventory-key loanId buyer) { 63 | "balance":amount 64 | }) 65 | ;; update new balance of the issuer in the inventory table 66 | (update loan-inventory-table (inventory-key loanId entityName){ 67 | "balance": (- issuerBalance amount) 68 | })) 69 | ;; update loan status at loans-table 70 | (update loans-table loanId { 71 | "status": ASSIGNED 72 | })) 73 | 74 | (defun sell-a-loan (txid loanId buyer seller amount) 75 | (with-read loan-inventory-table (inventory-key loanId seller) 76 | {"balance":= prev-seller-balance} 77 | (with-default-read loan-inventory-table (inventory-key loanId buyer) 78 | {"balance" : 0} 79 | {"balance":= prev-buyer-balance} 80 | (insert loan-history-table txid { 81 | "loanId":loanId, 82 | "buyer":buyer, 83 | "seller":seller, 84 | "amount": amount 85 | }) 86 | (update loan-inventory-table (inventory-key loanId seller) 87 | {"balance": (- prev-seller-balance amount)}) 88 | (write loan-inventory-table (inventory-key loanId buyer) 89 | {"balance": (+ prev-buyer-balance amount)})))) 90 | 91 | (defun read-a-loan (loanId) 92 | (read loans-table loanId)) 93 | 94 | (defun read-loan-tx () 95 | (map (txlog loans-table) (txids loans-table 0))) 96 | 97 | (defun read-all-loans () 98 | (select loans-table (constantly true))) 99 | 100 | (defun read-inventory-pair (key) 101 | {"inventory-key":key, "balance": (at 'balance (read loan-inventory-table key))} 102 | ) 103 | 104 | (defun read-loan-inventory () 105 | (map (read-inventory-pair) (keys loan-inventory-table))) 106 | 107 | (defun read-loans-with-status (status) 108 | (select loans-table (where "status" (= status)))) 109 | 110 | ) 111 | 112 | (create-table loans-table) 113 | (create-table loan-history-table) 114 | (create-table loan-inventory-table) 115 | -------------------------------------------------------------------------------- /pact/dapp-contracts/loans/loans.repl: -------------------------------------------------------------------------------- 1 | 2 | (env-keys ["loansadmin"]) 3 | (env-data { "loans-admin-keyset": 4 | { "keys": ["loansadmin"], "pred": "keys-all" } }) 5 | 6 | (begin-tx) 7 | (load "loans.pact") 8 | 9 | (commit-tx) 10 | ;(typecheck 'loans) 11 | 12 | (begin-tx) 13 | 14 | (use loans) 15 | 16 | (create-a-loan "loanId-1" "loan1" "Capital One" 50000) ;; loanId, loanName, entityName, amount 17 | 18 | 19 | 20 | (assign-a-loan "txid-1" "loanId-1" "buyer1" 10000) ;; loanId, buyer, amount 21 | (sell-a-loan "txid-2" "loanId-1" "buyer2" "buyer1" 2000) ;; loanId, seller, buyer, amount 22 | (sell-a-loan "txid-3" "loanId-1" "buyer3" "buyer1" 2000) ;; loanId, seller, buyer, amount 23 | 24 | 25 | (commit-tx) 26 | 27 | (begin-tx) 28 | 29 | (use loans) 30 | 31 | ;; Check history of the loan 32 | 33 | (read-loan-inventory) 34 | 35 | (read-loans-with-status INITIATED) 36 | (read-loans-with-status ASSIGNED) 37 | 38 | (commit-tx) 39 | -------------------------------------------------------------------------------- /pact/dapp-contracts/memory-wall/README.md: -------------------------------------------------------------------------------- 1 | # Memory Wall 2 | ![memory-wall](hello.png) 3 | The contract is a simple contract to engrave name to Kadena blockchain. 4 | 5 | ## Governance 6 | The contract is governed by a capability, "GOVERNANCE". The capability is guarded by the keyset, `hello-keyset` 7 | 8 | Learn more about Pact keysets [here](https://pact-language.readthedocs.io/en/latest/pact-reference.html#keysets-and-authorization) 9 | 10 | ## Tables 11 | The contract contains a name table to track users who greeted. 12 | - **name-table** : `name` 13 | 14 | Learn more about Pact tables [here](https://pact-language.readthedocs.io/en/latest/pact-reference.html#deftable) 15 | 16 | ## Functions 17 | 18 | ### here 19 | - Function to call that user was here: `user` 20 | - Adds a row with the key, the nth number to greet with the contract, to `name-table` and count up the row, `total` 21 | ``` 22 | (here "my-name") 23 | ``` 24 | 25 | ### lookup 26 | - Takes in the key and return the name: `key` 27 | - Used to fetch the names who greeted. 28 | 29 | Learn more about Pact functions [here](https://pact-language.readthedocs.io/en/latest/pact-reference.html#defun) 30 | 31 | ## Demo 32 | 33 | The Contract is deployed on Kadena Mainnet. Go sign your name [here](https://hello.chainweb.com/) 34 | -------------------------------------------------------------------------------- /pact/dapp-contracts/memory-wall/hello-world.pact: -------------------------------------------------------------------------------- 1 | (namespace "free") 2 | 3 | (module hello-world GOVERNANCE 4 | "A smart contract to greet the world." 5 | 6 | (defcap GOVERNANCE () 7 | (enforce-guard (keyset-ref-guard 'hello-keyset))) 8 | 9 | (defschema name-schema 10 | @doc "Name schema" 11 | @model [(invariant (!= name ""))] 12 | name) 13 | 14 | (deftable 15 | name-table:{name-schema}) 16 | 17 | (defun here (n:string) 18 | "Designed for /send calls. Leave your trace on Kadena mainnet!" 19 | (enforce (!= n "") "Name cannot be empty") 20 | (with-default-read name-table "total" 21 | {"name": 0} 22 | {"name":= total} 23 | (write name-table "total" {"name": (+ total 1)}) 24 | (write name-table (format "{}" [total]) {"name": n})) 25 | (format "{} was here." [n])) 26 | 27 | (defun lookup (key:string) 28 | (with-read name-table key 29 | {"name":= name} 30 | name)) 31 | ) 32 | 33 | (create-table name-table) 34 | -------------------------------------------------------------------------------- /pact/dapp-contracts/memory-wall/hello-world.repl: -------------------------------------------------------------------------------- 1 | 2 | (begin-tx) 3 | 4 | ;;Load Namespace Contract 5 | (env-data 6 | { 'ns-admin-keyset: ["admin"] 7 | , 'ns-operate-keyset: ["operate"] 8 | , 'ns-genesis-keyset: { "keys": [], "pred": "="} }) 9 | (load "../../dapp-contracts/namespaces/ns.pact") 10 | 11 | (commit-tx) 12 | 13 | (begin-tx) 14 | 15 | (env-data { 16 | "hello-keyset": { "keys": ["hello"], "pred": "keys-all" }}) 17 | (env-keys ["hello"]) 18 | 19 | (define-keyset 'hello-keyset (read-keyset 'hello-keyset)) 20 | (load "hello-world.pact") 21 | 22 | (commit-tx) 23 | 24 | (use free.hello-world) 25 | 26 | ;;Add greeters "Kadena", "Chainweb", "Mainnet". "20-chain" 27 | (expect "Kadena was here." "Kadena was here." (here "Kadena")) 28 | (expect "Chainweb was here." "Chainweb was here." (here "Chainweb")) 29 | (expect "Mainnet was here." "Mainnet was here." (here "Mainnet")) 30 | (expect "20-chain was here." "20-chain was here." (here "20-chain")) 31 | 32 | (expect "Kadena" "Kadena" (lookup "0")) 33 | (expect "Chainweb" "Chainweb" (lookup "1")) 34 | (expect "Mainnet" "Mainnet" (lookup "2")) 35 | (expect "20-chain" "20-chain" (lookup "3")) 36 | -------------------------------------------------------------------------------- /pact/dapp-contracts/memory-wall/hello.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kadena-io/developer-scripts/4285732c565098469cd0e079b9df1cef87ae6ec2/pact/dapp-contracts/memory-wall/hello.png -------------------------------------------------------------------------------- /pact/dapp-contracts/namespaces/ns.pact: -------------------------------------------------------------------------------- 1 | (define-keyset 'ns-admin-keyset (read-keyset 'ns-admin-keyset)) 2 | (define-keyset 'ns-operate-keyset (read-keyset 'ns-genesis-keyset)) 3 | 4 | (module ns GOVERNANCE 5 | "Administers definition of new namespaces in Chainweb." 6 | 7 | (defschema reg-entry 8 | admin-guard:guard 9 | active:bool) 10 | 11 | (deftable registry:{reg-entry}) 12 | 13 | (defcap GOVERNANCE () 14 | (enforce-keyset 'ns-admin-keyset)) 15 | 16 | (defcap OPERATE () 17 | (enforce-keyset 'ns-operate-keyset)) 18 | 19 | (defconst GUARD_SUCCESS (create-user-guard (success))) 20 | (defconst GUARD_FAILURE (create-user-guard (failure))) 21 | 22 | (defun success () 23 | true) 24 | (defun failure () 25 | (enforce false "Disabled")) 26 | 27 | (defun validate-name (name) 28 | (enforce (!= "" name) "Empty name not allowed") 29 | (enforce (< (length name) 64) "Name must be less than 64 characters long") 30 | (enforce (is-charset CHARSET_LATIN1 name) 31 | "Name must be in latin1 charset")) 32 | 33 | (defun validate:bool 34 | ( ns-name:string 35 | ns-admin:guard 36 | ) 37 | " Manages namespace install for Chainweb. Requires active row in registry \ 38 | \ for NS-NAME with guard matching NS-ADMIN." 39 | 40 | (validate-name ns-name) 41 | 42 | (with-default-read registry ns-name 43 | { 'admin-guard : ns-admin 44 | , 'active : false } 45 | { 'admin-guard := ag 46 | , 'active := is-active } 47 | 48 | (enforce is-active "Inactive or unregistered namespace") 49 | (enforce (= ns-admin ag) "Admin guard must match guard in registry") 50 | 51 | true)) 52 | 53 | (defun write-registry:string 54 | ( ns-name:string 55 | guard:guard 56 | active:bool 57 | ) 58 | " Write entry with GUARD and ACTIVE into registry for NAME. \ 59 | \ Guarded by operate keyset. " 60 | 61 | (with-capability (OPERATE) 62 | 63 | (validate-name ns-name) 64 | 65 | (write registry ns-name 66 | { 'admin-guard: guard 67 | , 'active: active }) 68 | 69 | "Register entry written")) 70 | 71 | (defun query:object{reg-entry} 72 | ( ns-name:string ) 73 | (read registry ns-name)) 74 | 75 | ) 76 | 77 | (create-table registry) 78 | 79 | (write-registry "kadena" 80 | (keyset-ref-guard 'ns-operate-keyset) true) 81 | (write-registry "user" GUARD_FAILURE true) 82 | (write-registry "free" GUARD_FAILURE true) 83 | 84 | (define-namespace "kadena" 85 | (keyset-ref-guard 'ns-operate-keyset) 86 | (keyset-ref-guard 'ns-operate-keyset)) 87 | 88 | (define-namespace "user" GUARD_SUCCESS GUARD_FAILURE) 89 | (define-namespace "free" GUARD_SUCCESS GUARD_FAILURE) 90 | ;;rotate to real operate keyset 91 | (define-keyset 'ns-operate-keyset (read-keyset 'ns-operate-keyset)) 92 | -------------------------------------------------------------------------------- /pact/dapp-contracts/namespaces/ns.repl: -------------------------------------------------------------------------------- 1 | (begin-tx) 2 | (env-data 3 | { 'ns-admin-keyset: ["admin"] 4 | , 'ns-operate-keyset: ["operate"] 5 | , 'ns-genesis-keyset: { "keys": [], "pred": "="} }) 6 | 7 | (load "ns.pact") 8 | (commit-tx) 9 | 10 | (env-namespace-policy false (ns.validate)) 11 | 12 | (begin-tx) 13 | (namespace 'user) 14 | (env-keys []) 15 | 16 | (module mod G 17 | (defcap G () (enforce false "disabled")) 18 | (defun foo () 1)) 19 | 20 | (namespace 'free) 21 | 22 | (module mod G 23 | (defcap G () (enforce false "disabled")) 24 | (defun foo () 2)) 25 | 26 | (expect-failure 27 | "Cannot bring kadena ns into scope w/o operate admin" 28 | (namespace 'kadena)) 29 | 30 | (env-keys ["operate"]) 31 | 32 | (namespace 'kadena) 33 | 34 | (module mod G 35 | (defcap G () (enforce false "disabled")) 36 | (defun foo () 3)) 37 | 38 | (commit-tx) 39 | 40 | (expect "user.mod works" 1 (user.mod.foo)) 41 | (expect "free.mod works" 2 (free.mod.foo)) 42 | (expect "kadena.mod works" 3 (kadena.mod.foo)) 43 | 44 | (begin-tx) 45 | (env-keys ["operate"]) 46 | (env-data 47 | { 'ns-admin-keyset: ["admin"] 48 | , 'ns-operate-keyset: ["operate"] }) 49 | 50 | (expect-failure "cannot redefine user" 51 | (define-namespace 'user ns.GUARD_FAILURE ns.GUARD_FAILURE)) 52 | (expect-failure "cannot redefine free" 53 | (define-namespace 'free ns.GUARD_FAILURE ns.GUARD_FAILURE)) 54 | (expect "can redefine kadena" 55 | "Namespace defined: kadena" 56 | (define-namespace 'kadena ns.GUARD_SUCCESS ns.GUARD_FAILURE)) 57 | 58 | (commit-tx) 59 | 60 | (begin-tx) 61 | (env-keys []) 62 | 63 | (namespace 'kadena) 64 | 65 | (module mod2 G 66 | (defcap G () (enforce false "disabled")) 67 | (defun foo () 4)) 68 | (commit-tx) 69 | 70 | (expect "kadena.mod2 works" 4 (kadena.mod2.foo)) 71 | 72 | (use ns) 73 | (env-keys ["operate"]) 74 | (expect-failure 75 | "cannot register empty name" 76 | (write-registry "" GUARD_SUCCESS true)) 77 | 78 | (expect-failure 79 | "cannot register >64 length name" 80 | (write-registry 81 | "1234567890123456789012345678901234567890123456789012345678901234567890" 82 | GUARD_SUCCESS true)) 83 | 84 | (expect-failure 85 | "must be latin1 charset" 86 | (write-registry "emilyπ" GUARD_SUCCESS true)) 87 | -------------------------------------------------------------------------------- /pact/dapp-contracts/pacty-parrot/README.md: -------------------------------------------------------------------------------- 1 | # Pacty Parrots 2 | 3 | ![parrots](pacty-parrots.png) 4 | 5 | Pacty Parrots is the first game contract on Kadena testnet. 6 | - 7 | 8 | ## Governance 9 | The contract is governed by a capability, "GOVERNANCE". The capability is guarded by the guard of the coin account, `contract-admins`. 10 | 11 | Learn more about Module Governance [here](https://pact-language.readthedocs.io/en/stable/pact-reference.html#generalized-module-governance) 12 | 13 | ## Tables 14 | The contract contains a user games table to track the user's activity and status. 15 | - **user-games-table** : `rounds-played` `coins-in` `coins-out` `total-rolls` `rounds` 16 | 17 | Learn more about Pact tables [here](https://pact-language.readthedocs.io/en/latest/pact-reference.html#deftable) 18 | 19 | ## Functions 20 | 21 | ### start-round 22 | - Transfers 5 tokens from the user account to parrot bank account: `account` 23 | - Calls `parrot-draw` function which calculates the amount and update the table with the result. 24 | ``` 25 | (start-round "test-user") 26 | ``` 27 | 28 | ### end-round 29 | - Transfers the round's earned tokens from parrot bank account to user account: `account` 30 | - Updates the user activity in the table. 31 | ``` 32 | (end-round "test-user") 33 | ``` 34 | 35 | Learn more about Pact functions [here](https://pact-language.readthedocs.io/en/latest/pact-reference.html#defun) 36 | 37 | ## Demo 38 | 39 | The Contract is deployed on Kadena Testnet, and is usable [here](https://pactyparrots.testnet.chainweb.com/) 40 | -------------------------------------------------------------------------------- /pact/dapp-contracts/pacty-parrot/pacty-parrots.pact: -------------------------------------------------------------------------------- 1 | (namespace "user") 2 | (module pacty-parrots GOVERNANCE 3 | 4 | (defcap GOVERNANCE () 5 | (enforce-guard (at 'guard (details 'contract-admins)))) 6 | 7 | 8 | (use coin) 9 | ; -------------------------------------------------------------------------- 10 | ; Schemas and Tables 11 | ; --------------------------------------------------------------------- 12 | (defschema user-games 13 | rounds-played:integer 14 | coins-in:decimal 15 | coins-out:decimal 16 | total-rolls:integer 17 | rounds:list) 18 | (deftable user-games-table:{user-games}) 19 | ; -------------------------------------------------------------------------- 20 | ; Constants and Capabilities 21 | ; -------------------------------------------------------------------------- 22 | (defconst ROUND_CLOSED:string "closed") 23 | (defconst ROUND_OPEN:string "open") 24 | (defconst PARROTS_ACCOUNT:string 'parrot-bank) 25 | (defconst PAYOUT_MATRIX:object { 26 | ;pirate and cop zero your score 27 | ;all other results are summed or multiplied if both outcomes the same 28 | ;Blue is worth 7 points 29 | "BB": 49, "BG": 13, "BP": 12, "BR": 11, "BT": 10, "BY": 9, "BI": 0, "BC": 0, 30 | "BF": 507, "BA": 207, "BK": 107, "BD": 57, "BM": 22, "BH": 37, "BS": 67, 31 | ;Green is worth 6 points 32 | "GB": 13, "GG": 36, "GP": 11, "GR": 10, "GT": 9, "GY": 8, "GI": 0, "GC": 0, 33 | "GF": 506, "GA": 206, "GK": 106, "GD": 56, "GM": 21, "GH": 36, "GS": 66, 34 | ;Purple is worth 5 points 35 | "PB": 12, "PG": 11, "PP": 25, "PR": 9, "PT": 8, "PY": 7, "PI": 0, "PC": 0, 36 | "PF": 505, "PA": 205, "PK": 105, "PD": 55, "PM": 20, "PH": 35, "PS": 65, 37 | ;Red is worth 4 points 38 | "RB": 11, "RG": 10, "RP": 9, "RR": 16, "RT": 7, "RY": 6, "RI": 0, "RC": 0, 39 | "RF": 504, "RA": 204, "RK": 104, "RD": 54, "RM": 19, "RH": 34, "RS": 64, 40 | ;Teal is worth 3 points 41 | "TB": 10, "TG": 9, "TP": 8, "TR": 7, "TT": 9, "TY": 5, "TI": 0, "TC": 0, 42 | "TF": 503, "TA": 203, "TK": 103, "TD": 53, "TM": 18, "TH": 33, "TS": 63, 43 | ;Yellow is worth 2 points 44 | "YB": 9, "YG": 8, "YP": 7, "YR": 6, "YT": 5, "YY": 4, "YI": 0, "YC": 0, 45 | "YF": 502, "YA": 202, "YK": 102, "YD": 52, "YM": 17, "YH": 32, "YS": 62, 46 | ;Cop zeros result 47 | "CB": 0, "CG": 0, "CP": 0, "CR": 0, "CT": 0, "CY": 0, "CI": 0, "CC": 0, 48 | "CF": 0, "CA": 0, "CK": 0, "CD": 0, "CM": 0, "CH": 0, "CS": 0, 49 | ;Pirate zeros result 50 | "IB": 0, "IG": 0, "IP": 0, "IR": 0, "IT": 0, "IY": 0, "II": 0, "IC": 0, 51 | "IF": 0, "IA": 0, "IK": 0, "ID": 0, "IM": 0, "IH": 0, "IS": 0, 52 | ;Guy Fieri is worth 500 points 53 | "FB": 507, "FG": 506, "FP": 505, "FR": 504, "FT": 503, "FY": 502, "FI": 0, "FC": 0, 54 | "FF": 250000, "FA": 700, "FK": 600, "FD": 550, "FM": 515, "FH": 530, "FS": 560, 55 | ;Nicolas Cage is worth 200 points 56 | "AB": 207, "AG": 206, "AP": 205, "AR": 204, "AT": 203, "AY": 202, "AI": 0, "AC": 0, 57 | "AF": 700, "AA": 40000, "AK": 300, "AD": 250, "AM": 215, "AH": 230, "AS": 260, 58 | ;Keanu Reeves is worth 100 points 59 | "KB": 107, "KG": 106, "KP": 105, "KR": 104, "KT": 103, "KY": 102, "KI": 0, "KC": 0, 60 | "KF": 600, "KA": 300, "KK": 10000, "KD": 150, "KM": 115, "KH": 130, "KS": 160, 61 | ;deal-with-it is worth 50 points 62 | "DB": 57, "DG": 56, "DP": 55, "DR": 54, "DT": 53, "DY": 52, "DI": 0, "DC": 0, 63 | "DF": 550, "DA": 250, "DK": 150, "DD": 2500, "DM": 65, "DH": 80, "DS": 110, 64 | ;moustache is worth 15 points 65 | "MB": 22, "MG": 21, "MP": 20, "MR": 19, "MT": 18, "MY": 17, "MI": 0, "MC": 0, 66 | "MF": 515, "MA": 215, "MK": 115, "MD": 65, "MM": 225, "MH": 45, "MS": 75, 67 | ;sherlock is worth 30 points 68 | "HB": 37, "HG": 36, "HP": 35, "HR": 34, "HT": 33, "HY": 32, "HI": 0, "HC": 0, 69 | "HF": 530, "HA": 230, "HK": 130, "HD": 80, "HM": 45, "HH": 900, "HS": 90, 70 | ;spy is worth 60 points 71 | "SB": 67, "SG": 66, "SP": 65, "SR": 64, "ST": 63, "SY": 62, "SI": 0, "SC": 0, 72 | "SF": 560, "SA": 260, "SK": 160, "SD": 110, "SM": 75, "SH": 90, "SS": 1200 73 | }) 74 | (defun parrots-guard:guard () (create-module-guard 'parrots-admin)) 75 | (defcap BET (account) 76 | "ensures only account holder is making the bets" 77 | (let ((g (at "guard" (details account)))) 78 | (enforce-guard g) 79 | ) 80 | ;true 81 | ) 82 | ; -------------------------------------------------------------------------- 83 | ; PACTY PARROTS CONTRACT -- main functions 84 | ; -------------------------------------------------------------------------- 85 | (defun start-round (account:string) 86 | @doc "5 COINS TO ENTER. Lets any user registered in coin contract initiate a round \ 87 | \ fails if round-id provided is not latest played and if round has ended \ 88 | \ user must pay 5 coins to enter" 89 | (with-capability (BET account) 90 | ;with-default-read to account for new users 91 | (with-default-read user-games-table account 92 | {"rounds-played": 0, 93 | "coins-in": 0.0, 94 | "coins-out": 0.0, 95 | "total-rolls": 0, 96 | "rounds": []} 97 | {"rounds-played":= rounds-played, 98 | "coins-in":= coins-in, 99 | "coins-out":= coins-out, 100 | "total-rolls":= total-rolls, 101 | "rounds":= rounds} 102 | ;make sure round-id supplied is same as length of current rounds-played 103 | ;len is 0 if its a new user 104 | ;(enforce (= round-id (length rounds)) "incorrect round id supplied") 105 | ;if not round 0, check prev round is closed 106 | (if (= (length rounds) 0) "" (enforce (= ROUND_CLOSED (at 2 (at (- (length rounds) 1) rounds))) "selected round must be open")) 107 | ;send money to coin contract 108 | ;enforces that the account exits in contract 109 | (transfer account PARROTS_ACCOUNT 5.0) 110 | (let* ((draw-result (parrot-draw account)) 111 | (points (at draw-result PAYOUT_MATRIX)) 112 | (status (if (= 0 points) ROUND_CLOSED ROUND_OPEN)) 113 | (new-round [[draw-result] points status])) 114 | (write user-games-table account 115 | {"rounds-played": (+ rounds-played 1), 116 | "coins-in": (+ coins-in 5.0), 117 | "coins-out": coins-out, 118 | "total-rolls": (+ total-rolls 1), 119 | "rounds": (+ rounds [new-round])} 120 | ) 121 | ) 122 | ) 123 | ) 124 | ) 125 | (defun continue-round (account:string) 126 | @doc "lets users registered in this contract continue an existing round \ 127 | \ must use a capability in this case as coin contract is not called to verify signer \ 128 | \ if user rolls a 0 point case it closes the round automatically" 129 | (with-capability (BET account) 130 | (with-read user-games-table account 131 | {"rounds":= rounds, "total-rolls":= total-rolls} 132 | ;(enforce false (length rounds)) 133 | ;make sure you are modifying the last element in the list of rounds 134 | ;(enforce (= round-id (- (length rounds) 1)) "incorrect round id supplied") 135 | ;make sure the round is open 136 | (enforce (= ROUND_OPEN (at 2 (at (- (length rounds) 1) rounds))) "selected round must be open") 137 | (let* ((round (at (- (length rounds) 1) rounds)) 138 | (draw-result (parrot-draw account)) 139 | (points (if (= 0 (at draw-result PAYOUT_MATRIX)) 0 (+ (at 1 round) (at draw-result PAYOUT_MATRIX)))) 140 | (status (if (= 0 points) ROUND_CLOSED ROUND_OPEN)) 141 | (new-round [(+ (at 0 round) [draw-result]) points status])) 142 | (update user-games-table account 143 | {"rounds": (+ (drop -1 rounds) [new-round]), 144 | "total-rolls": (+ total-rolls 1)} 145 | ) 146 | ) 147 | ) 148 | ) 149 | ) 150 | (defun end-round (account:string) 151 | @doc "let users registered in this contract end an existing round \ 152 | \ performs checks" 153 | (with-capability (BET account) 154 | (with-read user-games-table account 155 | {"rounds":= rounds, "coins-out":= coins-out} 156 | ;(enforce (= round-id (- (length rounds) 1)) "incorrect round id supplied") 157 | (enforce (= ROUND_OPEN (at 2 (at (- (length rounds) 1) rounds))) "selected round must be open") 158 | (let* ((round (at (- (length rounds) 1) rounds)) 159 | (points (at 1 round))) 160 | ;pay user if has got more than 0 points 161 | ;although its impossible in theory to have 0 points and a status of ROUND_OPEN 162 | (if (not (= 0 points)) (transfer PARROTS_ACCOUNT account (* points 1.0)) "") 163 | (update user-games-table account 164 | { 165 | "rounds": (+ (drop -1 rounds) [[(at 0 round) (at 1 round) ROUND_CLOSED]]), 166 | "coins-out": (+ coins-out (* points 1.0)) 167 | } 168 | ) 169 | ) 170 | ) 171 | ) 172 | ) 173 | ; -------------------------------------------------------------------------- 174 | ; HELPERS -> Draw functions 175 | ; -------------------------------------------------------------------------- 176 | (defun str-to-draw (str:string) 177 | @doc "HELPER: converts two digit string to single parrot outcome" 178 | (let ((draw-int (str-to-int str))) 179 | ;8% chance of blue parrot 180 | (if (and (>= draw-int 0) (<= draw-int 79)) "B" 181 | ;9% chance of green parrot 182 | (if (and (>= draw-int 80) (<= draw-int 169)) "G" 183 | ;10% chance of purple parrot 184 | (if (and (>= draw-int 170) (<= draw-int 269)) "P" 185 | ;11% chance of red parrot 186 | (if (and (>= draw-int 270) (<= draw-int 379)) "R" 187 | ;12% chance of teal parrot 188 | (if (and (>= draw-int 380) (<= draw-int 499)) "T" 189 | ;13% chance of yellow parrot 190 | (if (and (>= draw-int 500) (<= draw-int 629)) "Y" 191 | ;5% chance of pirate parrot 192 | (if (and (>= draw-int 630) (<= draw-int 679)) "I" 193 | ;5% chance of cop parrot 194 | (if (and (>= draw-int 680) (<= draw-int 729)) "C" 195 | ;7% chance of mustache parrot 196 | (if (and (>= draw-int 730) (<= draw-int 799)) "M" 197 | ;6% chance of sherlock parrot 198 | (if (and (>= draw-int 800) (<= draw-int 859)) "H" 199 | ;5% chance of deal-with-it parrot 200 | (if (and (>= draw-int 860) (<= draw-int 909)) "D" 201 | ;4% chance of spy parrot 202 | (if (and (>= draw-int 910) (<= draw-int 949)) "S" 203 | ;2.5% chance of keanu reeves parrot 204 | (if (and (>= draw-int 950) (<= draw-int 974)) "K" 205 | ;1.5% chance of nicolas cage parrot 206 | (if (and (>= draw-int 975) (<= draw-int 989)) "A" 207 | ;1% chance of fieri parrot 208 | (if (and (>= draw-int 990) (<= draw-int 999)) "F" ""))))))))))))))) 209 | ) 210 | ) 211 | ;need to restrict calling this function somehow... 212 | (defun parrot-draw (account:string) 213 | @doc "reads from (chain-data) to take in block-time, \ 214 | \ block-height, prev-block-hash hashes it, \ 215 | \ takes first three and last three digits of str-to-int base 64 to create double parrot outcome" 216 | ; (let* ((chain-seeds (chain-data)) 217 | ; (block-time-hash (hash (at "block-time" chain-seeds))) 218 | ; (prev-block-hash (hash (at "prev-block-hash" chain-seeds))) 219 | ; (block-height-hash (hash (at "block-height"chain-seeds))) 220 | ; (master-hash-int (str-to-int 64 (hash (+ block-height-hash (+ prev-block-hash block-time-hash))))) 221 | (let* (;(block-time-hash (hash (at "block-time" (chain-data)))) 222 | (prev-block-hash (at "prev-block-hash" (chain-data))) 223 | ;(block-height-hash (format "{}" [(at "block-height"(chain-data))])) 224 | ;(master-hash-int (str-to-int 64 (hash (+ block-height-hash (+ prev-block-hash block-time-hash))))) 225 | (master-hash-int (str-to-int 64 (hash (+ prev-block-hash (take 20 account))))) 226 | ;(master-hash-int (str-to-int 64 (hash prev-block-hash))) 227 | (master-hash-str (format "{}" [master-hash-int])) 228 | (first-parrot (take 3 master-hash-str)) 229 | (second-parrot (take -3 master-hash-str))) 230 | (+ (str-to-draw first-parrot) (str-to-draw second-parrot)) 231 | ) 232 | ) 233 | ; -------------------------------------------------------------------------- 234 | ; UTILS -> for fetching data ( /local calls ) 235 | ; -------------------------------------------------------------------------- 236 | (defun get-table (account:string) 237 | (read user-games-table account) 238 | ) 239 | (defun get-users () 240 | (keys user-games-table) 241 | ) 242 | (defun info-helper (payouts:object result:string) 243 | (at result payouts) 244 | ) 245 | (defun get-payout-matrix () 246 | PAYOUT_MATRIX 247 | ) 248 | (defun get-current-round-info (account:string) 249 | (with-read user-games-table account 250 | { "rounds":= rounds } 251 | (let* ((current-round (at (- (length rounds) 1) rounds)) 252 | (rolls (at 0 current-round)) 253 | (points (map (info-helper PAYOUT_MATRIX) rolls)) 254 | (total (fold (+) 0 points))) 255 | (format "your rolls and corresponding points from round {} are: {} {} and total points: {}" [(+ (- (length rounds) 1) 1) rolls points total]) 256 | ) 257 | ) 258 | ) 259 | ) 260 | 261 | 262 | 263 | (create-table user-games-table) 264 | 265 | ;this can be done post deploy 266 | ; by reading the guard from the coin contract 267 | ; (transfer-create "contract-admins" PARROTS_ACCOUNT (parrots-guard) 1000000.0) 268 | -------------------------------------------------------------------------------- /pact/dapp-contracts/pacty-parrot/pacty-parrots.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kadena-io/developer-scripts/4285732c565098469cd0e079b9df1cef87ae6ec2/pact/dapp-contracts/pacty-parrot/pacty-parrots.png -------------------------------------------------------------------------------- /pact/dapp-contracts/pacty-parrot/pacty-parrots.repl: -------------------------------------------------------------------------------- 1 | (begin-tx) 2 | 3 | ;;Load Namespace Contract 4 | (env-data 5 | { 'ns-admin-keyset: ["admin"] 6 | , 'ns-operate-keyset: ["operate"] 7 | , 'ns-genesis-keyset: { "keys": [], "pred": "="} }) 8 | (load "../../dapp-contracts/namespaces/ns.pact") 9 | 10 | ;;Load Coin Contract 11 | (load "../../dapp-contracts/coin-contract/fungible-v2.pact") 12 | (load "../../dapp-contracts/coin-contract/coin.pact") 13 | (create-table coin.coin-table) 14 | 15 | (env-data { "contract-admins" : ["contract-admins"]}) 16 | (test-capability (COINBASE)) 17 | (coinbase 'contract-admins (read-keyset 'contract-admins) 1000000.0) 18 | 19 | (commit-tx) 20 | 21 | (begin-tx) 22 | 23 | (env-data { "player-1" : ["keys1"], 24 | "parrot-admin-keyset": ["croesus-key"], 25 | "parrot-bank": ["parrot-bank-key"]}) 26 | 27 | (env-keys ["keys1", "keys2", 28 | "keys3", "croesus-key", 29 | "p1-keys", "p2-keys", 30 | "p3-keys", "parrot-bank-key"]) 31 | 32 | (define-keyset "parrots-admin-keyset" (read-keyset "parrot-admin-keyset")) 33 | (env-keys ["contract-admins"]) 34 | 35 | (load "pacty-parrots.pact") 36 | 37 | (commit-tx) 38 | 39 | (begin-tx) 40 | 41 | (use coin) 42 | (use user.pacty-parrots) 43 | 44 | ;;Fund Parrot Bank 45 | (env-sigs [ 46 | { 47 | 'key: "contract-admins", 48 | 'caps: [ 49 | (coin.TRANSFER 'contract-admins PARROTS_ACCOUNT 100000.0) 50 | ] 51 | }]) 52 | 53 | (transfer-create "contract-admins" PARROTS_ACCOUNT (parrots-guard) 100000.0) 54 | 55 | ;;Fund player-1 56 | (test-capability (COINBASE)) 57 | (env-data { "player-1" : ["keys1"]}) 58 | (coinbase 'player-1 (read-keyset 'player-1) 10.0) 59 | 60 | (commit-tx) 61 | 62 | (begin-tx) 63 | 64 | ;important for our randomness... 65 | (env-chain-data { 66 | "chain-id": "0", 67 | "block-height": 20, 68 | "prev-block-hash": "dsdjhhaaaaahhakjsxxxxxkjd===haskjdhahhhskd"}) 69 | 70 | (commit-tx) 71 | 72 | (begin-tx) 73 | 74 | (use user.pacty-parrots) 75 | (use coin) 76 | 77 | (env-data { "player-1" : ["keys1"] }) 78 | 79 | (env-keys ["keys1", "keys2", 80 | "keys3", "croesus-key", 81 | "p1-keys", "p2-keys", 82 | "p3-keys", "parrot-bank-key"]) 83 | 84 | (env-chain-data { 85 | "chain-id": "0", 86 | "block-height": 20, 87 | "prev-block-hash": "dsdjhhhhakjssssasasasasskdhahhhska"}) 88 | 89 | (env-sigs [ 90 | { 91 | 'key: "keys1", 92 | 'caps: [ 93 | (coin.TRANSFER "player-1" "parrot-bank" 15.0) 94 | (BET "player-1") 95 | ] 96 | }]) 97 | 98 | ;;First Round 99 | 100 | (expect "10.0" 10.0 (get-balance "player-1")) 101 | 102 | ;;First Roll 103 | (start-round "player-1") 104 | (get-current-round-info "player-1") 105 | 106 | (expect "5.0" 5.0 (get-balance "player-1")) 107 | 108 | ;;Second Roll 109 | (env-chain-data { 110 | "chain-id": "0", 111 | "block-height": 21, 112 | "prev-block-hash": "dsdjhhhhakjssssasasasasskdhahhhskb"}) 113 | 114 | (continue-round "player-1") 115 | (get-current-round-info "player-1") 116 | 117 | ;;Third Roll 118 | (env-chain-data { 119 | "chain-id": "0", 120 | "block-height": 22, 121 | "prev-block-hash": "dsdjkjdhjsadsadasadsdsjhdshjskc"}) 122 | 123 | (continue-round "player-1") 124 | (get-current-round-info "player-1") 125 | 126 | (env-sigs [ 127 | { 128 | 'key: "keys1", 129 | 'caps: [ 130 | (coin.TRANSFER "parrot-bank" "player-1" 331.0) 131 | (BET "player-1") 132 | ] 133 | }]) 134 | 135 | (end-round "player-1") 136 | 137 | (get-current-round-info "player-1") 138 | (get-table "player-1") 139 | 140 | (expect "316.0" 316.0 (get-balance "player-1")) 141 | 142 | 143 | ;;Second Round 144 | (env-chain-data { 145 | "chain-id": "0", 146 | "block-height": 20, 147 | "prev-block-hash": "dsdjkjdhjsadsadsdsjhdshjskd"}) 148 | 149 | ;Zeroed out and round closed due to cop result. 150 | (start-round "player-1") 151 | (get-current-round-info "player-1") 152 | (expect-failure "Round is closed" (continue-round "player-1")) 153 | (expect "311.0" 311.0 (get-balance "player-1")) 154 | 155 | (commit-tx) 156 | -------------------------------------------------------------------------------- /pact/dapp-contracts/stablecoin/README.md: -------------------------------------------------------------------------------- 1 | # Stablecoin 2 | This contract is an example of stablecoin implementation in Pact. 3 | 4 | 5 | ## Governance 6 | The contract is governed by a capability, "GOVERNANCE". The capability is guarded by the guard of the coin account, `my-token-admin`. 7 | 8 | Learn more about Module Governance [here](https://pact-language.readthedocs.io/en/stable/pact-reference.html#generalized-module-governance) 9 | 10 | ## Tables 11 | The contract contains a token table to track the guard and balance of the user. 12 | - **token-table** : `balance` `guard` 13 | 14 | Learn more about Pact tables [here](https://pact-language.readthedocs.io/en/latest/pact-reference.html#deftable) 15 | 16 | ## Functions 17 | 18 | ### mint 19 | - Mints my-token as an operator: `accountId` `amount` `guard` 20 | ``` 21 | (mint `operator` 1.0 (read-keyset 'op-keyset)) 22 | ``` 23 | 24 | ### burn 25 | - Burn my-token as an operator: `accountId` `amount` 26 | ``` 27 | (burn `operator` 1.0 ) 28 | ``` 29 | 30 | ### create-account 31 | - Create a new account of my-token: `account` `guard` 32 | - Fails if account already exists 33 | ``` 34 | (create-account "sender00" (read-keyset "sender00-guard")) 35 | ``` 36 | 37 | ### transfer 38 | - transfers `amount` of my-token from `sender` to `receiver`: `sender` `receiver` `amount` 39 | - Fails if `receiver` does not exist 40 | - This function is an implementation of the interface, [fungible-v1](https://github.com/kadena-io/chainweb-node/blob/master/pact/coin-contract/fungible-v1.pact). Learn more about Pact interface [here](https://pact-language.readthedocs.io/en/latest/pact-reference.html#interfaces) 41 | 42 | ``` 43 | (transfer "sender00" "sender01" 0.001) 44 | ``` 45 | 46 | ### transfer-create 47 | - transfers `amount` of my-token from `sender` to `receiver`: `sender` `receiver` `receiver-guard` `amount` 48 | - If `receiver` does not exist, creates a new account with `receiver-guard` 49 | - This function is an implementation of the interface, [fungible-v1](https://github.com/kadena-io/chainweb-node/blob/master/pact/coin-contract/fungible-v1.pact). Learn more about Pact interface [here](https://pact-language.readthedocs.io/en/latest/pact-reference.html#interfaces) 50 | 51 | ``` 52 | (transfer-create "sender00" "sender11" (read-keyset 'sender11-keyset) 1.0) 53 | ``` 54 | 55 | Learn more about Pact functions [here](https://pact-language.readthedocs.io/en/latest/pact-reference.html#defun) 56 | 57 | ### transfer-crosschain 58 | - Transfers `amount` of my-token from `sender` to a `receiver` on a `target-chain`: `sender` `receiver` `receiver-guard` `target-chain` `amount` 59 | - If `receiver` does not exist, creates a new account with `receiver-guard` 60 | - This function is an implementation of the interface, [fungible-v1](https://github.com/kadena-io/chainweb-node/blob/master/pact/coin-contract/fungible-v1.pact). Learn more about Pact interface [here](https://pact-language.readthedocs.io/en/latest/pact-reference.html#interfaces) 61 | 62 | ``` 63 | (transfer-crosschain "sender00" "sender01" "10" 1.0) 64 | ``` 65 | 66 | ### rotate 67 | - Rotates the account guard: `account` 68 | 69 | ``` 70 | (rotate "sender00" (read-keyset 'my-new-keyset)) 71 | ``` 72 | 73 | ### get-balance 74 | - Returns the balance of my-token on `account` : `account` 75 | 76 | ``` 77 | (get-balance "sender00") 78 | ``` 79 | 80 | ### details 81 | - Returns the account name, guard and balance of my-token on `account` : `account` 82 | 83 | ``` 84 | (details "sender00") 85 | ``` 86 | -------------------------------------------------------------------------------- /pact/dapp-contracts/stablecoin/my-token.pact: -------------------------------------------------------------------------------- 1 | (namespace "user") 2 | (module my-token GOVERNANCE 3 | 4 | 5 | (implements fungible-v2) 6 | 7 | ; -------------------------------------------------------------------------- 8 | ; Schemas and Tables 9 | 10 | (defschema token-schema 11 | @doc " An account, holding a token balance. \ 12 | \ \ 13 | \ ROW KEY: accountId. " 14 | balance:decimal 15 | ;NOTE: guard will always also contain the my-token-ops account keyset 16 | ; which will allow the operator to perform transfers for users 17 | ; predicate: 'keys-any' 18 | ; pub-keys: [ops-pub-key, user-pub-key] 19 | guard:guard 20 | ) 21 | (deftable token-table:{token-schema}) 22 | 23 | ; -------------------------------------------------------------------------- 24 | ; Capatilibites 25 | 26 | (defcap GOVERNANCE 27 | () 28 | 29 | @doc " Give the admin full access to call and upgrade the module. " 30 | (enforce-guard (at 'guard (coin.details "my-token-admin"))) 31 | ) 32 | 33 | (defcap OPS 34 | () 35 | 36 | @doc " Let operations account mint, burn, and transfer tokens " 37 | 38 | (enforce-guard (at 'guard (coin.details "my-token-ops"))) 39 | ) 40 | 41 | (defcap ACCOUNT_GUARD 42 | ( accountId:string ) 43 | @doc " Look up the guard for an account, required to debit from that account. " 44 | (enforce-guard (at 'guard (read token-table accountId ["guard"]))) 45 | ) 46 | 47 | (defcap DEBIT 48 | ( sender:string ) 49 | 50 | @doc " Capability to perform debiting operations. " 51 | 52 | (enforce-guard (at 'guard (read token-table sender ['guard]))) 53 | (enforce (!= sender "") "Invalid sender.") 54 | ) 55 | 56 | (defcap CREDIT 57 | ( receiver:string ) 58 | 59 | @doc " Capability to perform crediting operations. " 60 | 61 | (enforce (!= receiver "") "Invalid receiver.") 62 | ) 63 | 64 | (defcap TRANSFER:bool 65 | ( sender:string 66 | receiver:string 67 | amount:decimal ) 68 | 69 | @doc " Capability to perform transfer between two accounts. " 70 | 71 | @managed amount TRANSFER-mgr 72 | 73 | (enforce (!= sender receiver) "Sender cannot be the receiver.") 74 | (enforce-unit amount) 75 | (enforce (> amount 0.0) "Transfer amount must be positive.") 76 | (compose-capability (DEBIT sender)) 77 | (compose-capability (CREDIT receiver)) 78 | ) 79 | 80 | (defun TRANSFER-mgr:decimal 81 | ( managed:decimal 82 | requested:decimal ) 83 | 84 | (let ((newbal (- managed requested))) 85 | (enforce (>= newbal 0.0) 86 | (format "TRANSFER exceeded for balance {}" [managed])) 87 | newbal 88 | ) 89 | ) 90 | 91 | ; -------------------------------------------------------------------------- 92 | ; Constants 93 | 94 | (defconst DECIMALS 0 95 | " Specifies the minimum denomination for token transactions. ") 96 | 97 | (defconst ACCOUNT_ID_CHARSET CHARSET_LATIN1 98 | " Allowed character set for account IDs. ") 99 | 100 | (defconst ACCOUNT_ID_PROHIBITED_CHARACTER "$") 101 | 102 | (defconst ACCOUNT_ID_MIN_LENGTH 3 103 | " Minimum character length for account IDs. ") 104 | 105 | (defconst ACCOUNT_ID_MAX_LENGTH 256 106 | " Maximum character length for account IDs. ") 107 | 108 | ; -------------------------------------------------------------------------- 109 | ; Utilities 110 | 111 | (defun validate-account-id 112 | ( accountId:string ) 113 | 114 | @doc " Enforce that an account ID meets charset and length requirements. " 115 | 116 | (enforce 117 | (is-charset ACCOUNT_ID_CHARSET accountId) 118 | (format 119 | "Account ID does not conform to the required charset: {}" 120 | [accountId])) 121 | 122 | (enforce 123 | (not (contains accountId ACCOUNT_ID_PROHIBITED_CHARACTER)) 124 | (format "Account ID contained a prohibited character: {}" [accountId])) 125 | 126 | (let ((accountLength (length accountId))) 127 | 128 | (enforce 129 | (>= accountLength ACCOUNT_ID_MIN_LENGTH) 130 | (format 131 | "Account ID does not conform to the min length requirement: {}" 132 | [accountId])) 133 | 134 | (enforce 135 | (<= accountLength ACCOUNT_ID_MAX_LENGTH) 136 | (format 137 | "Account ID does not conform to the max length requirement: {}" 138 | [accountId])) 139 | ) 140 | ) 141 | 142 | (defun mint 143 | ( accountId:string 144 | amount:decimal 145 | guard:keyset ) 146 | 147 | (with-capability (OPS) 148 | ;; Allow ops keyset to mint tokens 149 | (validate-account-id accountId) 150 | (enforce (> amount 0.0) "Credit amount must be positive.") 151 | (enforce-unit amount) 152 | 153 | (with-default-read token-table accountId 154 | { "balance" : 0.0 155 | , "guard" : guard 156 | } 157 | { "balance" := balance 158 | , "guard" := currentGuard 159 | } 160 | (enforce (= currentGuard guard) "Account guards do not match.") 161 | 162 | (write token-table accountId 163 | { "balance" : (+ balance amount) 164 | , "guard" : currentGuard 165 | } 166 | ) 167 | ) 168 | ) 169 | ) 170 | 171 | (defun burn 172 | ( accountId:string 173 | amount:decimal ) 174 | 175 | (with-capability (OPS) 176 | ;; Allow ops keyset to burn tokens 177 | 178 | (validate-account-id accountId) 179 | (enforce (> amount 0.0) "Debit amount must be positive.") 180 | (enforce-unit amount) 181 | 182 | (with-read token-table accountId 183 | { "balance" := balance } 184 | 185 | (enforce (<= amount balance) "Insufficient funds.") 186 | 187 | (update token-table accountId 188 | { "balance" : (- balance amount) } 189 | ) 190 | ) 191 | ) 192 | ) 193 | 194 | ;; ; -------------------------------------------------------------------------- 195 | ;; ; Fungible-v2 Implementation 196 | 197 | (defun transfer-create:string 198 | ( sender:string 199 | receiver:string 200 | receiver-guard:guard 201 | amount:decimal ) 202 | 203 | @doc " Transfer to an account, creating it if it does not exist. " 204 | 205 | (with-capability (TRANSFER sender receiver amount) 206 | (debit sender amount) 207 | (credit receiver receiver-guard amount) 208 | ) 209 | ) 210 | 211 | (defun transfer:string 212 | ( sender:string 213 | receiver:string 214 | amount:decimal ) 215 | 216 | @doc " Transfer to an account, failing if the account does not exist. " 217 | 218 | 219 | (with-read token-table receiver 220 | { "guard" := guard } 221 | (transfer-create sender receiver guard amount) 222 | ) 223 | ) 224 | 225 | (defun debit 226 | ( accountId:string 227 | amount:decimal ) 228 | 229 | @doc " Decrease an account balance. Internal use only. " 230 | 231 | 232 | (validate-account-id accountId) 233 | (enforce (> amount 0.0) "Debit amount must be positive.") 234 | (enforce-unit amount) 235 | (require-capability (DEBIT accountId)) 236 | 237 | (with-read token-table accountId 238 | { "balance" := balance } 239 | 240 | (enforce (<= amount balance) "Insufficient funds.") 241 | 242 | (update token-table accountId 243 | { "balance" : (- balance amount) } 244 | ) 245 | ) 246 | ) 247 | 248 | (defun credit 249 | ( accountId:string 250 | guard:guard 251 | amount:decimal ) 252 | 253 | @doc " Increase an account balance. Internal use only. " 254 | 255 | 256 | (validate-account-id accountId) 257 | (enforce (> amount 0.0) "Credit amount must be positive.") 258 | (enforce-unit amount) 259 | (require-capability (CREDIT accountId)) 260 | 261 | (with-default-read token-table accountId 262 | { "balance" : 0.0 263 | , "guard" : guard 264 | } 265 | { "balance" := balance 266 | , "guard" := currentGuard 267 | } 268 | (enforce (= currentGuard guard) "Account guards do not match.") 269 | 270 | (write token-table accountId 271 | { "balance" : (+ balance amount) 272 | , "guard" : currentGuard 273 | } 274 | ) 275 | ) 276 | ) 277 | 278 | (defschema crosschain-schema 279 | @doc " Schema for yielded value in cross-chain transfers " 280 | receiver:string 281 | receiver-guard:guard 282 | amount:decimal 283 | ) 284 | 285 | (defpact transfer-crosschain:string 286 | ( sender:string 287 | receiver:string 288 | receiver-guard:guard 289 | target-chain:string 290 | amount:decimal ) 291 | 292 | (step 293 | (with-capability (DEBIT sender) 294 | 295 | (validate-account-id sender) 296 | (validate-account-id receiver) 297 | 298 | (enforce (!= "" target-chain) "empty target-chain") 299 | (enforce (!= (at 'chain-id (chain-data)) target-chain) 300 | "cannot run cross-chain transfers to the same chain") 301 | 302 | (enforce (> amount 0.0) 303 | "transfer quantity must be positive") 304 | 305 | (enforce-unit amount) 306 | 307 | ;; Step 1 - debit sender account on current chain 308 | (debit sender amount) 309 | 310 | (let 311 | (( 312 | crosschain-details:object{crosschain-schema} 313 | { "receiver" : receiver 314 | , "receiver-guard" : receiver-guard 315 | , "amount" : amount 316 | } 317 | )) 318 | (yield crosschain-details target-chain) 319 | ) 320 | ) 321 | ) 322 | 323 | (step 324 | (resume 325 | { "receiver" := receiver 326 | , "receiver-guard" := receiver-guard 327 | , "amount" := amount 328 | } 329 | ;; Step 2 - credit receiver account on target chain 330 | (with-capability (CREDIT receiver) 331 | (credit receiver receiver-guard amount) 332 | ) 333 | ) 334 | ) 335 | ) 336 | 337 | (defun get-balance:decimal 338 | ( account:string ) 339 | 340 | (at 'balance (read token-table account ['balance])) 341 | ) 342 | 343 | (defun details:object{fungible-v2.account-details} 344 | ( account:string ) 345 | 346 | (with-read token-table account 347 | { "balance" := balance 348 | , "guard" := guard 349 | } 350 | { "account" : account 351 | , "balance" : balance 352 | , "guard" : guard 353 | } 354 | ) 355 | ) 356 | 357 | (defun precision:integer 358 | () 359 | 360 | DECIMALS 361 | ) 362 | 363 | (defun enforce-unit:bool 364 | ( amount:decimal ) 365 | 366 | @doc " Enforce the minimum denomination for token transactions. " 367 | 368 | (enforce 369 | (= (floor amount DECIMALS) amount) 370 | (format "Amount violates minimum denomination: {}" [amount]) 371 | ) 372 | ) 373 | 374 | (defun create-account:string 375 | ( account:string 376 | guard:guard ) 377 | 378 | @doc " Create a new account. " 379 | 380 | 381 | (insert token-table account 382 | { "balance" : 0.0 383 | , "guard" : guard 384 | } 385 | ) 386 | ) 387 | 388 | (defun rotate:string 389 | ( account:string 390 | new-guard:guard ) 391 | 392 | @doc " Rotate guard for a given account " 393 | 394 | (with-capability (OPS) 395 | 396 | (with-read token-table account 397 | { "guard" := oldGuard } 398 | 399 | (enforce-guard oldGuard) 400 | (enforce-guard new-guard) 401 | 402 | (update token-table account 403 | { "guard" : new-guard } 404 | ) 405 | ) 406 | ) 407 | ) 408 | 409 | ) 410 | 411 | ; uncomment this line if you deploy with changes to the table schema 412 | ; or are deploying for the first time (ie to mainnet or different chain) 413 | (create-table token-table) 414 | -------------------------------------------------------------------------------- /pact/dapp-contracts/stablecoin/my-token.repl: -------------------------------------------------------------------------------- 1 | (begin-tx) 2 | 3 | ;;Load Namespace Contract 4 | (env-data 5 | { 'ns-admin-keyset: ["admin"] 6 | , 'ns-operate-keyset: ["operate"] 7 | , 'ns-genesis-keyset: { "keys": [], "pred": "="} }) 8 | (load "../../dapp-contracts/namespaces/ns.pact") 9 | 10 | ;;Load Coin Contract 11 | (load "../../dapp-contracts/coin-contract/fungible-v2.pact") 12 | (load "../../dapp-contracts/coin-contract/coin.pact") 13 | (create-table coin.coin-table) 14 | 15 | (commit-tx) 16 | 17 | (env-keys ["admin"]) 18 | (env-data { 19 | "admin": { "keys": ["my-token"], "pred": "keys-all" }, 20 | "ops": {"keys": ["ops"], "pred": "keys-all"}}) 21 | 22 | (begin-tx) 23 | (coin.create-account "my-token-admin" (read-keyset 'admin)) 24 | (coin.create-account "my-token-ops" (read-keyset 'ops)) 25 | (env-keys ["my-token"]) 26 | (load "my-token.pact") 27 | (commit-tx) 28 | 29 | (env-data { "account0-ks": 30 | { "keys": ["my-token"], "pred": "keys-all" } }) 31 | 32 | 33 | (env-data { 34 | "account0-ks": 35 | { "keys": ["account0"], "pred": "keys-all" }, 36 | "sender00-guard": 37 | { "keys": ["sender00"], "pred": "keys-all" }, 38 | "sender01-guard": 39 | { "keys": ["sender01"], "pred": "keys-all" } 40 | }) 41 | 42 | (begin-tx) 43 | (use user.my-token) 44 | ;;mint token 45 | 46 | (env-keys ["ops"]) 47 | 48 | ;;Balance before mint 49 | (expect-failure "account0 doesn't exist" (get-balance "account0")) 50 | 51 | ;;mint token 52 | (mint "account0" 5.0 (read-keyset "account0-ks")) 53 | 54 | ;;Balance after mint 55 | (expect "5.0" 5.0 (get-balance "account0")) 56 | 57 | ;;mint failure 58 | (env-keys []) 59 | (expect-failure 60 | "No OPS signature" 61 | (mint "account0" 5.0 (read-keyset "account0-ks"))) 62 | 63 | 64 | ;;create account 65 | (create-account "sender00" (read-keyset "sender00-guard")) 66 | 67 | ;;transfer 68 | 69 | ;;Balance before transfer 70 | (expect "5.0" 5.0 (get-balance "account0")) 71 | (expect "0.0" 0.0 (get-balance "sender00")) 72 | 73 | ;;transfer 74 | (env-sigs [{'key: "account0", 'caps: [(TRANSFER "account0" "sender00" 1.0)]}]) 75 | (transfer "account0" "sender00" 1.0) 76 | 77 | ;;Balance after transfer 78 | (expect "4.0" 4.0 (get-balance "account0")) 79 | (expect "1.0" 1.0 (get-balance "sender00")) 80 | 81 | 82 | ;;transfer-create 83 | 84 | ;;Balance before transfer-create 85 | (expect "4.0" 4.0 (get-balance "account0")) 86 | (expect-failure "sender01 doesn't exist" (get-balance "sender01")) 87 | 88 | ;;transfer-create 89 | (env-sigs [{'key: "account0", 'caps: [(TRANSFER "account0" "sender01" 1.0)]}]) 90 | (transfer-create "account0" "sender01" (read-keyset 'sender01-guard) 1.0) 91 | 92 | ;;Balance after transfer-create 93 | (expect "3.0" 3.0 (get-balance "account0")) 94 | (expect "1.0" 1.0 (get-balance "sender01")) 95 | 96 | ;;burn token 97 | 98 | ;;add OPS sig 99 | (env-keys ["ops"]) 100 | 101 | ;;Balance before burn 102 | (expect "3.0" 3.0 (get-balance "account0")) 103 | ;;burn 104 | (burn "account0" 1.0) 105 | ;;Balance after burn 106 | (expect "2.0" 2.0 (get-balance "account0")) 107 | 108 | ;;remove OPS sig 109 | (env-keys []) 110 | (expect-failure 111 | "No OPS signature" 112 | (burn "account0" 1.0)) 113 | 114 | (commit-tx) 115 | -------------------------------------------------------------------------------- /pact/dapp-contracts/todomvc/README.md: -------------------------------------------------------------------------------- 1 | # Todos 2 | 3 | ![todos](todomvc.png) 4 | 5 | ## Governance 6 | The contract is governed by a keyset, "loans-admin-keyset" 7 | 8 | Learn more about Pact keysets [here](https://pact-language.readthedocs.io/en/latest/pact-reference.html#keysets-and-authorization) 9 | 10 | ## Tables 11 | The contract contains a todo table to track the todo list and manage the status of each items. 12 | - **todo-table** : `title` `completed` `deleted` 13 | 14 | Learn more about Pact tables [here](https://pact-language.readthedocs.io/en/latest/pact-reference.html#deftable) 15 | 16 | ## Functions 17 | 18 | ### new-todo 19 | - Add a new todo item to the table: `id` `title` 20 | ``` 21 | (new-todo "todo-0" "Learn about Pact!") 22 | ``` 23 | 24 | ### toggle-todo-status 25 | - Toggle the status of the todo item: `id` 26 | - Updates the item's `completed` field to it's oppisite state. 27 | ``` 28 | (toggle-todo-status "todo-0") 29 | ``` 30 | 31 | ### edit-todo 32 | - Edit the title of the todo item: `id` `title` 33 | - Updates the item's `title` field to the new title. 34 | ``` 35 | (edit-todo "todo-0" "Learn about Kadena!") 36 | ``` 37 | 38 | ### delete-todo 39 | - Delete the todo item from the todo list: `id` 40 | - Update the item's `delete` field to `true` 41 | ``` 42 | (delete-todo "todo-0") 43 | ``` 44 | 45 | Learn more about Pact functions [here](https://pact-language.readthedocs.io/en/latest/pact-reference.html#defun) 46 | 47 | ## Demo 48 | 49 | The step by step tutorial on how this contract will work with the frontend can be found [here](https://pactlang.org/intermediate/pact-and-javascript/) 50 | Also, take a look at the project github [here](https://github.com/kadena-io/pact-todomvc) 51 | -------------------------------------------------------------------------------- /pact/dapp-contracts/todomvc/todomvc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kadena-io/developer-scripts/4285732c565098469cd0e079b9df1cef87ae6ec2/pact/dapp-contracts/todomvc/todomvc.png -------------------------------------------------------------------------------- /pact/dapp-contracts/todomvc/todos.pact: -------------------------------------------------------------------------------- 1 | ;; 2 | ;; todos smart contract 3 | ;; 4 | 5 | ;; admin keyset definition 6 | (define-keyset 'todo-admin-keyset 7 | (read-keyset "todo-admin-keyset")) 8 | 9 | ;; todos module, administered by keyset 10 | (module todos 'todo-admin-keyset 11 | " Smart contract module for TODO-MVC pact app. \ 12 | \ Tables: \ 13 | \ todo -- holds todo entries" 14 | 15 | ;; todo schema and table 16 | (defschema todo 17 | "Row type for todos." 18 | title:string 19 | completed:bool 20 | deleted:bool ) 21 | 22 | (deftable todo-table:{todo}) 23 | 24 | ;; 25 | ;; API functions 26 | ;; 27 | 28 | (defun new-todo (id title) 29 | "Create new todo with ENTRY and DATE." 30 | (insert todo-table id { 31 | "title": title, 32 | "completed": false, 33 | "deleted": false }) 34 | ) 35 | 36 | (defun toggle-todo-status (id:string) 37 | "Toggle completed status flag for todo at ID." 38 | (with-read todo-table id { 39 | "completed":= state 40 | } 41 | (update todo-table id { 42 | "completed": (not state) }))) 43 | 44 | (defun edit-todo (id:string title) 45 | "Update todo ENTRY at ID." 46 | (update todo-table id { 47 | "title": title })) 48 | 49 | (defun delete-todo (id:string) 50 | "Delete todo title at ID (by setting deleted flag)." 51 | (update todo-table id { 52 | "deleted": true })) 53 | 54 | (defun read-todo:object (id:string) 55 | "Read a single todo" 56 | (+ {'id: id} (read todo-table id))) 57 | 58 | (defun read-todos:[object{todo}] () 59 | "Read all todos." 60 | (map (read-todo) (keys todo-table))) 61 | ) 62 | 63 | (create-table todo-table) 64 | ;done 65 | -------------------------------------------------------------------------------- /pact/dapp-contracts/todomvc/todos.repl: -------------------------------------------------------------------------------- 1 | (env-keys ["todoadmin"]) 2 | (env-data { "todo-admin-keyset": 3 | { "keys": ["todoadmin"], "pred": "keys-all" } }) 4 | 5 | (begin-tx) 6 | (load "todos.pact") 7 | (commit-tx) 8 | 9 | (typecheck "todos") 10 | 11 | (use todos) 12 | 13 | ;;Create a new todo 14 | (new-todo "1" "wash") 15 | 16 | ;;Test if todo was created 17 | (expect "Returns the todo object with id-1" {"completed": false, "deleted": false, "id": "1", "title": "wash"} (read-todo "1")) 18 | 19 | ;;Fail if todo to be edited does not exist. 20 | (expect-failure "No todo exists with id of 2." (edit-todo "2" "clean")) 21 | -------------------------------------------------------------------------------- /pact/gas-station/README.md: -------------------------------------------------------------------------------- 1 | # Gas Station 2 | 3 | These are two types of gas stations that can be easily built on Kadena. 4 | - Gas Guards 5 | - free-x-chain-gas 6 | - Gas Payer 7 | - covid-gas-station 8 | 9 | ## Gas Guard 10 | - Gas Guards are general "guards". Coin accounts guarded by these guards will only be used for paying gas under generalized limitations. 11 | - Gas Guards use "guards" module. 12 | - "free-x-chain-gas" account is one of the gas stations built with gas guards. 13 | 14 | ## Gas Payer 15 | - Gas Payer are a customized "module" that will build a gas station. The module uses a gas-payer-v1 interface. 16 | - The gas station will be defined inside the customized module. 17 | - This type allows setting up a gas station for specific purpose such as "function","number of functions", "tx type", etc. 18 | - "covid-gas-station" account is one of the gas stations built with gas guards. 19 | 20 | Medium Article 21 | -------------------------------------------------------------------------------- /pact/gas-station/gas-guard/create-gas-station.pact: -------------------------------------------------------------------------------- 1 | (use util.guards1) 2 | (coin.create-account "gas-station" 3 | (guard-all [ 4 | (create-user-guard (coin.gas-only)) 5 | (max-gas-price 0.00000001) 6 | (max-gas-limit 400) ])) 7 | 8 | -------------------------------------------------------------------------------- /pact/gas-station/gas-guard/guards/guards.pact: -------------------------------------------------------------------------------- 1 | ;; guards.pact 2 | 3 | (namespace 'util) 4 | 5 | (module guards AUTONOMOUS 6 | 7 | "REDEPLOYING - Functions for implementing various user guards." 8 | 9 | (defcap AUTONOMOUS () 10 | (enforce false "Non-upgradeable")) 11 | 12 | (defun after-date:guard (date:time) 13 | "Guard to enforce chain time is after DATE." 14 | (create-user-guard (enforce-after-date date))) 15 | 16 | (defun enforce-after-date:bool (date:time) 17 | (enforce-time date "after" 18 | (> (chain-time) date))) 19 | 20 | 21 | (defun at-after-date:guard (date:time) 22 | "Guard to enforce chain time is at or after DATE." 23 | (create-user-guard (enforce-at-after-date date))) 24 | 25 | (defun enforce-at-after-date:bool (date:time) 26 | (enforce-time date "at or after" 27 | (>= (chain-time) date))) 28 | 29 | 30 | (defun before-date:guard (date:time) 31 | "Guard to enforce chain time is before DATE." 32 | (create-user-guard (enforce-before-date date))) 33 | 34 | (defun enforce-before-date:bool (date:time) 35 | (enforce-time date "before" 36 | (< (chain-time) date))) 37 | 38 | 39 | (defun at-before-date:guard (date:time) 40 | "Guard to enforce chain time is at or before DATE." 41 | (create-user-guard (enforce-at-before-date date))) 42 | 43 | (defun enforce-at-before-date:bool (date:time) 44 | (enforce-time date "at or before" 45 | (<= (chain-time) date))) 46 | 47 | 48 | (defun enforce-time:bool (date:time msg:string test:bool) 49 | (enforce test 50 | (format "Chain time must be {} {}" [msg date]))) 51 | 52 | (defun chain-time:time () 53 | (at 'block-time (chain-data))) 54 | 55 | (defun guard-and:guard (a:guard b:guard) 56 | "Guard to enforce both A and B." 57 | (create-user-guard (enforce-and a b))) 58 | 59 | (defun enforce-and:bool (a:guard b:guard) 60 | (enforce-guard a) 61 | (enforce-guard b)) 62 | 63 | (defun guard-or:guard (a:guard b:guard) 64 | "Guard to enforce A or B." 65 | (create-user-guard (enforce-or a b))) 66 | 67 | (defun enforce-or:bool (a:guard b:guard) 68 | (enforce-one 69 | (format "Enforce {} or {}" [a b]) 70 | [(enforce-guard a) 71 | (enforce-guard b)])) 72 | 73 | ) 74 | -------------------------------------------------------------------------------- /pact/gas-station/gas-guard/guards/guards.repl: -------------------------------------------------------------------------------- 1 | ;; guards.repl 2 | (enforce-pact-version "3.5.0") 3 | 4 | (env-data 5 | { 'util-ns-users: ["util-ns-user"] 6 | , 'util-ns-admin: ["util-ns-admin"] 7 | }) 8 | (env-keys ["util-ns-user", "util-ns-admin"]) 9 | 10 | (begin-tx) 11 | (load "../util/util-ns.pact") 12 | (commit-tx) 13 | 14 | 15 | (begin-tx) 16 | (load "guards.pact") 17 | (commit-tx) 18 | 19 | (use util.guards) 20 | 21 | (module T G 22 | (defcap G () true) 23 | (defconst JAN (time "2020-01-01T00:00:00Z")) 24 | (defconst FEB (time "2020-02-01T00:00:00Z")) 25 | ) 26 | 27 | ;; JAN chain time 28 | (env-chain-data { 'block-time: JAN }) 29 | 30 | ;; test JAN vs FEB 31 | (expect-failure 32 | "after-date fails" "must be after" 33 | (enforce-guard 34 | (after-date FEB))) 35 | 36 | (expect-failure 37 | "at-after-date fails" "must be at or after" 38 | (enforce-guard 39 | (at-after-date FEB))) 40 | 41 | (expect 42 | "before succeeds" true 43 | (enforce-guard 44 | (before-date FEB))) 45 | 46 | (expect 47 | "at-before succeeds" true 48 | (enforce-guard 49 | (at-before-date FEB))) 50 | 51 | ;; test JAN vs JAN 52 | 53 | (expect-failure 54 | "after-date fails on same" "must be after" 55 | (enforce-guard 56 | (after-date JAN))) 57 | 58 | (expect 59 | "at-after-date succeeds on same" true 60 | (enforce-guard 61 | (at-after-date JAN))) 62 | 63 | (expect-failure 64 | "before fails on same" "must be before" 65 | (enforce-guard 66 | (before-date JAN))) 67 | 68 | (expect 69 | "at-before succeeds on same" true 70 | (enforce-guard 71 | (at-before-date JAN))) 72 | 73 | ;; FEB chain time 74 | 75 | (env-chain-data { 'block-time: FEB }) 76 | 77 | ;; test FEB vs JAN 78 | 79 | (expect 80 | "after-date succeeds" true 81 | (enforce-guard 82 | (after-date JAN))) 83 | 84 | (expect 85 | "at-after-date succeeds" true 86 | (enforce-guard 87 | (at-after-date JAN))) 88 | 89 | (expect-failure 90 | "before fails" "must be before" 91 | (enforce-guard 92 | (before-date JAN))) 93 | 94 | (expect-failure 95 | "at-before fails" "must be at or before" 96 | (enforce-guard 97 | (at-before-date JAN))) 98 | 99 | ;; test compositions 100 | 101 | (env-keys ["yes"]) 102 | (env-data { "yes": ["yes"], "no": ["no"] }) 103 | (define-keyset 'yes) 104 | (define-keyset 'no) 105 | 106 | (expect 107 | "guard-and succeeds" true 108 | (enforce-guard 109 | (guard-and 110 | (keyset-ref-guard "yes") 111 | (after-date JAN)))) 112 | 113 | (expect-failure 114 | "guard-and fails" "must be after" 115 | (enforce-guard 116 | (guard-and 117 | (keyset-ref-guard "yes") 118 | (after-date FEB)))) 119 | 120 | (expect 121 | "guard-or succeeds" true 122 | (enforce-guard 123 | (guard-or 124 | (after-date JAN) (after-date JAN)))) 125 | -------------------------------------------------------------------------------- /pact/gas-station/gas-guard/guards/guards1.pact: -------------------------------------------------------------------------------- 1 | ;; guards1.pact 2 | 3 | (namespace 'util) 4 | 5 | (module guards1 'util-ns-admin 6 | "************************WARNING************************\ 7 | \ This module is currently governed by 'util-ns-admin \ 8 | \ and should not be in use until the governance is \ 9 | \ replaced with AUTONOMOUS, meaning that the module \ 10 | \ will be non-upgradable. \ 11 | \ ******************************************************\ 12 | \ Functions for implementing gas guards." 13 | 14 | (defcap AUTONOMOUS () 15 | (enforce false "Non-upgradeable")) 16 | 17 | (defun guard-all:guard (guards:[guard]) 18 | "Create a guard that only succeeds if every guard in GUARDS is successfully enforced." 19 | (enforce (< 0 (length guards)) "Guard list cannot be empty") 20 | (create-user-guard (enforce-guard-all guards))) 21 | 22 | (defun enforce-guard-all:bool (guards:[guard]) 23 | "Enforces all guards in GUARDS" 24 | (map (enforce-guard) guards) 25 | ) 26 | 27 | (defun guard-any:guard (guards:[guard]) 28 | "Create a guard that succeeds if at least one guard in GUARDS is successfully enforced." 29 | (enforce (< 0 (length guards)) "Guard list cannot be empty") 30 | (create-user-guard (enforce-guard-any guards))) 31 | 32 | (defun enforce-guard-any:bool (guards:[guard]) 33 | "Will succeed if at least one guard in GUARDS is successfully enforced." 34 | (enforce (< 0 35 | (length 36 | (filter 37 | (= true) 38 | (map (try-enforce-guard) guards)))) 39 | "None of the guards passed") 40 | ) 41 | 42 | (defun try-enforce-guard (g:guard) 43 | (try false (enforce-guard g)) 44 | ) 45 | 46 | (defun max-gas-notional:guard (gasNotional:decimal) 47 | "Guard to enforce gas price * gas limit is smaller than or equal to GAS" 48 | (create-user-guard 49 | (enforce-below-or-at-gas-notional gasNotional))) 50 | 51 | (defun enforce-below-gas-notional (gasNotional:decimal) 52 | (enforce (< (chain-gas-notional) gasNotional) 53 | (format "Gas Limit * Gas Price must be smaller than {}" [gasNotional]))) 54 | 55 | (defun enforce-below-or-at-gas-notional (gasNotional:decimal) 56 | (enforce (<= (chain-gas-notional) gasNotional) 57 | (format "Gas Limit * Gas Price must be smaller than or equal to {}" [gasNotional]))) 58 | 59 | (defun max-gas-price:guard (gasPrice:decimal) 60 | "Guard to enforce gas price is smaller than or equal to GAS PRICE" 61 | (create-user-guard 62 | (enforce-below-or-at-gas-price gasPrice))) 63 | 64 | (defun enforce-below-gas-price:bool (gasPrice:decimal) 65 | (enforce (< (chain-gas-price) gasPrice) 66 | (format "Gas Price must be smaller than {}" [gasPrice]))) 67 | 68 | (defun enforce-below-or-at-gas-price:bool (gasPrice:decimal) 69 | (enforce (<= (chain-gas-price) gasPrice) 70 | (format "Gas Price must be smaller than or equal to {}" [gasPrice]))) 71 | 72 | (defun max-gas-limit:guard (gasLimit:integer) 73 | "Guard to enforce gas limit is smaller than or equal to GAS LIMIT" 74 | (create-user-guard 75 | (enforce-below-or-at-gas-limit gasLimit))) 76 | 77 | (defun enforce-below-gas-limit:bool (gasLimit:integer) 78 | (enforce (< (chain-gas-limit) gasLimit) 79 | (format "Gas Limit must be smaller than {}" [gasLimit]))) 80 | 81 | (defun enforce-below-or-at-gas-limit:bool (gasLimit:integer) 82 | (enforce (<= (chain-gas-limit) gasLimit) 83 | (format "Gas Limit must be smaller than or equal to {}" [gasLimit]))) 84 | 85 | (defun chain-gas-price () 86 | "Return gas price from chain-data" 87 | (at 'gas-price (chain-data))) 88 | 89 | (defun chain-gas-limit () 90 | "Return gas limit from chain-data" 91 | (at 'gas-limit (chain-data))) 92 | 93 | (defun chain-gas-notional () 94 | "Return gas limit * gas price from chain-data" 95 | (* (chain-gas-price) (chain-gas-limit))) 96 | ) 97 | 98 | -------------------------------------------------------------------------------- /pact/gas-station/gas-guard/guards/guards1.repl: -------------------------------------------------------------------------------- 1 | ;; guards1.repl 2 | (enforce-pact-version "3.5.0") 3 | 4 | (env-data 5 | { 'util-ns-users: ["util-ns-user"] 6 | , 'util-ns-admin: ["util-ns-admin"] 7 | }) 8 | (env-keys ["util-ns-user", "util-ns-admin"]) 9 | 10 | (begin-tx) 11 | (load "../util/util-ns.pact") 12 | (commit-tx) 13 | 14 | 15 | (begin-tx) 16 | (load "guards1.pact") 17 | (commit-tx) 18 | 19 | (begin-tx) 20 | (use util.guards1) 21 | 22 | ;;set chain gas price to 0.0005 and chain gas limit to 500 23 | (env-chain-data { 24 | "gas-price": 0.0005, 25 | "gas-limit": 500 26 | }) 27 | 28 | ;;Test max-gas-notional 29 | (expect 30 | (format "chain-gas-notional {} is smaller than max-gas-notional {}" 31 | [(chain-gas-notional), 0.5]) 32 | true 33 | (enforce-guard (max-gas-notional 0.5))) 34 | 35 | (expect 36 | (format "chain-gas-notional {} is at than max-gas-notional {}" 37 | [(chain-gas-notional), 0.25]) 38 | true 39 | (enforce-guard (max-gas-notional 0.25))) 40 | 41 | (expect-failure 42 | (format "chain-gas-notional {} is bigger than than max-gas-notional {}" 43 | [(chain-gas-notional), 0.24]) 44 | "Gas Limit * Gas Price must be smaller than or equal to 0.24" 45 | (enforce-guard (max-gas-notional 0.24))) 46 | 47 | ;;Test max-gas-price 48 | (expect 49 | (format "chain-gas-price {} is smaller than max-gas-price {}" 50 | [(chain-gas-price) 0.00051]) 51 | true 52 | (enforce-guard (max-gas-price 0.00051))) 53 | 54 | (expect 55 | (format "chain-gas-price {} is at max-gas-price {}" 56 | [(chain-gas-price) 0.0005]) 57 | true 58 | (enforce-guard (max-gas-price 0.0005))) 59 | 60 | (expect-failure 61 | (format "chain-gas-price {} is bigger than max-gas-price {}" 62 | [(chain-gas-price) 0.00049]) 63 | "Gas Price must be smaller than or equal to 0.00049" 64 | (enforce-guard (max-gas-price 0.00049))) 65 | 66 | ;;Test max-gas-limit 67 | (expect 68 | (format "chain-gas-limit {} is smaller than max-gas-limit {}" 69 | [(chain-gas-limit) 600]) 70 | true 71 | (enforce-guard (max-gas-limit 600))) 72 | 73 | (expect 74 | (format "chain-gas-limit {} is at max-gas-limit {}" 75 | [(chain-gas-limit) 500]) 76 | true 77 | (enforce-guard (max-gas-limit 500))) 78 | 79 | (expect-failure 80 | (format "chain-gas-limit {} is bigger than max-gas-limit {}" 81 | [(chain-gas-limit) 400]) 82 | "Gas Limit must be smaller than or equal to 400" 83 | (enforce-guard (max-gas-limit 400))) 84 | 85 | ;;Set env-data with bob-ks and ks 86 | (env-data {"bob-ks": ["bob"], "ks": ["ks"]}) 87 | ;;Sign bob-ks 88 | (env-keys ["bob"]) 89 | 90 | ;;Examples of successful and failing guards for testing `guard-any` and `guard-all` 91 | (expect "Bob-ks is signed" 92 | true 93 | (enforce-guard (read-keyset 'bob-ks))) 94 | (expect "chain-gas-price is smaller than max-gas-price" 95 | true 96 | (enforce-guard (max-gas-price 0.0006))) 97 | (expect "chain-gas-limit is smaller than max-gas-limit" 98 | true 99 | (enforce-guard (max-gas-limit 600))) 100 | (expect-failure "ks is not signed" 101 | "Keyset failure (keys-all): [ks]" 102 | (enforce-guard (read-keyset 'ks))) 103 | (expect-failure "chain-gas-price is bigger than max-gas-price" 104 | "Gas Price must be smaller than or equal to 0.0004" 105 | (enforce-guard (max-gas-price 0.0004))) 106 | (expect-failure "chain-gas-limit is bigger than max-gas-limit" 107 | "Gas Limit must be smaller than or equal to 300" 108 | (enforce-guard (max-gas-limit 300))) 109 | 110 | ;;Test guard-all 111 | (expect-failure "guard-all cannot take in an empty list" 112 | "Guard list cannot be empty" 113 | (guard-all [])) 114 | 115 | (expect-failure "0/3 of the guard enforcements succeed" 116 | "Keyset failure (keys-all): [ks]" 117 | (enforce-guard (guard-all [ 118 | (read-keyset 'ks) 119 | (max-gas-price 0.0004) 120 | (max-gas-limit 300) 121 | ]))) 122 | 123 | (expect-failure "1/3 of the guard enforcements succeed" 124 | "Gas Price must be smaller than or equal to 0.0004" 125 | (enforce-guard (guard-all [ 126 | (read-keyset 'bob-ks) 127 | (max-gas-price 0.0004) 128 | (max-gas-limit 300) 129 | ]))) 130 | 131 | (expect-failure "2/3 of the guard enforcements succeed" 132 | "Gas Limit must be smaller than or equal to 300" 133 | (enforce-guard (guard-all [ 134 | (read-keyset 'bob-ks) 135 | (max-gas-price 0.0006) 136 | (max-gas-limit 300) 137 | ]))) 138 | 139 | (expect "3/3 of the guard enforcements succeed" 140 | true 141 | (enforce-guard (guard-all [ 142 | (read-keyset 'bob-ks) 143 | (max-gas-price 0.0006) 144 | (max-gas-limit 600) 145 | ]))) 146 | 147 | ;;Test guard-any 148 | (expect-failure "guard-any cannot take in an empty list" 149 | "Guard list cannot be empty" 150 | (guard-any [])) 151 | 152 | (expect-failure "0/3 of the guard enforcements succeed" 153 | "None of the guards passed" 154 | (enforce-guard (guard-any [ 155 | (read-keyset 'ks) 156 | (max-gas-price 0.0004) 157 | (max-gas-limit 300) 158 | ]))) 159 | 160 | (expect "1/3 of the guard enforcements succeed" 161 | true 162 | (enforce-guard (guard-any [ 163 | (read-keyset 'bob-ks) 164 | (max-gas-price 0.0004) 165 | (max-gas-limit 300) 166 | ]))) 167 | 168 | (expect "2/3 of the guard enforcements succeed" 169 | true 170 | (enforce-guard (guard-any [ 171 | (read-keyset 'bob-ks) 172 | (max-gas-price 0.0006) 173 | (max-gas-limit 300) 174 | ]))) 175 | 176 | (expect "3/3 of the guard enforcements succeed" 177 | true 178 | (enforce-guard (guard-any [ 179 | (read-keyset 'bob-ks) 180 | (max-gas-price 0.0006) 181 | (max-gas-limit 600) 182 | ]))) 183 | 184 | (commit-tx) 185 | -------------------------------------------------------------------------------- /pact/gas-station/gas-payer/covid-gas-station.pact: -------------------------------------------------------------------------------- 1 | (namespace 'user) 2 | 3 | (module covid-gas-station GOVERNANCE 4 | (defcap GOVERNANCE () 5 | "makes sure only admin account can update the smart contract" 6 | (enforce-guard (at 'guard (coin.details "covid-admin"))) 7 | ; true 8 | ) 9 | 10 | (implements gas-payer-v1) 11 | (use coin) 12 | 13 | (defschema gas 14 | balance:decimal 15 | guard:guard) 16 | 17 | (deftable ledger:{gas}) 18 | 19 | (defcap GAS_PAYER:bool 20 | ( user:string 21 | limit:integer 22 | price:decimal 23 | ) 24 | (enforce (= "exec" (at "tx-type" (read-msg))) "Inside an exec") 25 | (enforce (= 1 (length (at "exec-code" (read-msg)))) "Tx of only one pact function") 26 | (enforce (= "(user.covid." (take 12 (at 0 (at "exec-code" (read-msg))))) "only user.covid smart contract") 27 | (compose-capability (ALLOW_GAS)) 28 | ) 29 | 30 | (defcap ALLOW_GAS () true) 31 | 32 | (defun create-gas-payer-guard:guard () 33 | (create-user-guard (gas-payer-guard)) 34 | ) 35 | 36 | (defun gas-payer-guard () 37 | (require-capability (GAS)) 38 | (require-capability (ALLOW_GAS)) 39 | ) 40 | ) 41 | 42 | (coin.transfer-create "covid-admin" "covid-gas-payer" (user.covid-gas-station.create-gas-payer-guard) 2.0) 43 | -------------------------------------------------------------------------------- /pact/gas-station/gas-payer/gas-payer-v1-reference.pact: -------------------------------------------------------------------------------- 1 | (define-keyset 'operate (read-keyset 'gas-payer-operate)) 2 | 3 | (namespace 'user) 4 | (module gas-payer-v1-reference G 5 | (defcap G () true) 6 | (implements gas-payer-v1) 7 | (use coin) 8 | 9 | (defschema gas 10 | balance:decimal 11 | guard:guard) 12 | 13 | (deftable ledger:{gas}) 14 | 15 | (defcap GAS_PAYER:bool 16 | ( user:string 17 | limit:integer 18 | price:decimal 19 | ) 20 | (with-read ledger user { 'balance:= bal, 'guard:= g} 21 | (let ((amount (* limit price))) 22 | (enforce (>= bal amount) 23 | (format "Insufficient gas balance: {} < {}" [bal amount])) 24 | (enforce-guard g) 25 | (update ledger user 26 | {'balance: (- bal amount)}) 27 | (compose-capability (ALLOW_GAS)))) 28 | ) 29 | 30 | (defcap FUND_USER () 31 | (enforce-keyset 'operate)) 32 | 33 | (defcap ALLOW_GAS () true) 34 | 35 | (defun create-gas-payer-guard:guard () 36 | (create-user-guard (gas-payer-guard)) 37 | ) 38 | 39 | (defun gas-payer-guard () 40 | (require-capability (GAS)) 41 | (require-capability (ALLOW_GAS)) 42 | ) 43 | 44 | (defun fund-user 45 | (user:string guard:guard amount:decimal) 46 | (with-capability (FUND_USER) 47 | (with-default-read ledger user 48 | { 'balance: 0.0, 'guard: guard } 49 | { 'balance:= bal, 'guard:= g } 50 | (enforce (= guard g) "Guards don't match") 51 | (write ledger user 52 | { 'balance: (+ bal amount) 53 | , 'guard: guard 54 | } 55 | ))) 56 | ) 57 | 58 | (defun user-balance ( user:string ) 59 | (at 'balance (read ledger user))) 60 | 61 | ) 62 | 63 | (create-table ledger) 64 | -------------------------------------------------------------------------------- /pact/gas-station/gas-payer/gas-payer-v1-reference.repl: -------------------------------------------------------------------------------- 1 | (begin-tx) 2 | ;;Load Namespace Contract 3 | (env-data 4 | { 'ns-admin-keyset: ["admin"] 5 | , 'ns-operate-keyset: ["operate"] 6 | , 'ns-genesis-keyset: { "keys": [], "pred": "="} }) 7 | (load "../../dapp-contracts/namespaces/ns.pact") 8 | 9 | ;;Load Coin Contract 10 | (load "../../dapp-contracts/coin-contract/fungible-v2.pact") 11 | (load "../../dapp-contracts/coin-contract/coin.pact") 12 | (create-table coin.coin-table) 13 | 14 | ;;Load Gas Payer V1 15 | (load "gas-payer-v1.pact") 16 | (commit-tx) 17 | 18 | (env-data {'gas-payer-operate: ["operate"]}) 19 | (env-keys ["operate"]) 20 | 21 | (load "gas-payer-v1-reference.pact") 22 | 23 | (verify "user.gas-payer-v1-reference") 24 | 25 | (env-data {'alice: ["alice"], "buyer": ["buyer"]}) 26 | (env-keys ["operate","buyer"]) 27 | 28 | 29 | (test-capability (coin.COINBASE)) 30 | (coin.coinbase "gas-payer" (create-gas-payer-guard) 10.0) 31 | (coin.create-account "gas-buyer" (read-keyset 'buyer)) 32 | 33 | (expect-failure "gas should not be allowed pre-funding" 34 | (test-capability (GAS_PAYER "alice" 1 1.0))) 35 | 36 | (fund-user "alice" (read-keyset "alice") 10.0) 37 | 38 | (expect-failure "gas should not be allowed without signature" 39 | (test-capability (GAS_PAYER "alice" 1 1.0))) 40 | 41 | (env-sigs [{"key": "alice", "caps": [(GAS_PAYER "alice" 2 1.0)]}]) 42 | 43 | (expect-failure "gas should not be allowed on mismatched sig cap" 44 | (test-capability (GAS_PAYER "alice" 1 1.0))) 45 | 46 | ;; note installing this way not because managed but to demonstrate 47 | ;; key pinning for security. 48 | (env-sigs [{"key": "alice", "caps": [(GAS_PAYER "alice" 1 1.0)]}]) 49 | 50 | (test-capability (coin.GAS)) 51 | 52 | (expect-failure 53 | "Cannot unlock payer account" 54 | "not granted" 55 | (coin.buy-gas "gas-payer" 1.0)) 56 | 57 | (expect "GAS_PAYER acquired" 58 | "Capability acquired" 59 | (test-capability (GAS_PAYER "alice" 1 1.0))) 60 | 61 | (expect "gas is allowed after cap is installed" 62 | true 63 | (require-capability (ALLOW_GAS))) 64 | 65 | (expect "user balance updated" 66 | 9.0 67 | (user-balance "alice")) 68 | 69 | (expect "Gas buy succeeds" 70 | "Write succeeded" 71 | (coin.buy-gas "gas-payer" 1.0)) 72 | -------------------------------------------------------------------------------- /pact/gas-station/gas-payer/gas-payer-v1.pact: -------------------------------------------------------------------------------- 1 | (interface gas-payer-v1 2 | 3 | (defcap GAS_PAYER:bool 4 | ( user:string 5 | limit:integer 6 | price:decimal 7 | ) 8 | @doc 9 | " Provide a capability indicating that declaring module supports \ 10 | \ gas payment for USER for gas LIMIT and PRICE. Functionality \ 11 | \ should require capability (coin.FUND_TX), and should validate \ 12 | \ the spend of (limit * price), possibly updating some database \ 13 | \ entry. \ 14 | \ Should compose capability required for 'create-gas-payer-guard'." 15 | @model 16 | [ (property (user != "")) 17 | (property (limit > 0)) 18 | (property (price > 0.0)) 19 | ] 20 | ) 21 | 22 | (defun create-gas-payer-guard:guard () 23 | @doc 24 | " Provide a guard suitable for controlling a coin account that can \ 25 | \ pay gas via GAS_PAYER mechanics. Generally this is accomplished \ 26 | \ by having GAS_PAYER compose an unparameterized, unmanaged capability \ 27 | \ that is required in this guard. Thus, if coin contract is able to \ 28 | \ successfully acquire GAS_PAYER, the composed 'anonymous' cap required \ 29 | \ here will be in scope, and gas buy will succeed." 30 | ) 31 | 32 | ) 33 | -------------------------------------------------------------------------------- /pact/utility/README.md: -------------------------------------------------------------------------------- 1 | # Utility Scripts 2 | 3 | - Safe Transfer 4 | - Safe Rotate 5 | 6 | ## Safe Transfer 7 | - Used for transfers between two accounts that one owns. 8 | - Prevents loss of token due to a transfer to a wrong destination. 9 | 10 | ## Safe Rotate 11 | - Used for coin account guard rotation. 12 | - Prevents loss of ownership to the coin account due to a guard rotation to a wrong guard. 13 | -------------------------------------------------------------------------------- /pact/utility/safe-rotate.pact: -------------------------------------------------------------------------------- 1 | (use coin) 2 | (let* ((acct:string "rotest") 3 | (bal:decimal (coin.get-balance acct)) 4 | ) 5 | (coin.rotate acct (read-keyset "ks")) 6 | (coin.transfer acct 7 | "croesus" 8 | bal) 9 | ) 10 | -------------------------------------------------------------------------------- /pact/utility/safe-transfer.pact: -------------------------------------------------------------------------------- 1 | ;;Smart Transfer 2 | (coin.transfer-create "alice" "bob" (read-keyset "ks") 200.1) 3 | (coin.transfer "bob" "alice" 0.1) 4 | --------------------------------------------------------------------------------