├── rust-toolchain ├── .gitignore ├── compiled ├── repo.wasm ├── repo-deployed.wasm └── RepoContract.json ├── .cargo └── config ├── wasm-install.sh ├── Cargo.toml ├── .travis.yml ├── contract ├── Cargo.toml └── src │ └── lib.rs ├── src └── repo.rs ├── README.md ├── LICENSE └── Cargo.lock /rust-toolchain: -------------------------------------------------------------------------------- 1 | nightly-2018-07-24 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | **/*.rs.bk 3 | .vscode 4 | contract/Cargo.lock 5 | -------------------------------------------------------------------------------- /compiled/repo.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openethereum/pwasm-repo-contract/HEAD/compiled/repo.wasm -------------------------------------------------------------------------------- /.cargo/config: -------------------------------------------------------------------------------- 1 | [target.wasm32-unknown-unknown] 2 | rustflags = [ 3 | "-C", "link-args=-z stack-size=65536", 4 | ] 5 | -------------------------------------------------------------------------------- /compiled/repo-deployed.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openethereum/pwasm-repo-contract/HEAD/compiled/repo-deployed.wasm -------------------------------------------------------------------------------- /wasm-install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # this script is intended to be used from .travis.yml 4 | 5 | curl -sL https://storage.googleapis.com/wasm-llvm/builds/linux/$WATERFALL_BUILD/wasm-binaries.tbz2 | tar xvkj 6 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "pwasm-repo-bin" 3 | version = "0.1.0" 4 | authors = ["Alexey Frolov "] 5 | 6 | [dependencies] 7 | pwasm-ethereum = "0.6" 8 | pwasm-abi = "0.1" 9 | pwasm-repo-contract = { path = "contract" } 10 | 11 | [lib] 12 | crate-type = ["cdylib"] 13 | path = "src/repo.rs" 14 | 15 | [profile.release] 16 | panic = "abort" 17 | lto = true 18 | opt-level = "z" 19 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: rust 2 | rust: 3 | - nightly 4 | cache: cargo 5 | before_install: 6 | - rustup target add wasm32-unknown-unknown 7 | - command -v wasm-build || cargo install pwasm-utils-cli --bin wasm-build 8 | - WATERFALL_BUILD=31834 ./wasm-install.sh 9 | script: 10 | - cargo test --manifest-path="contract/Cargo.toml" --features std 11 | - ./build.sh 12 | - wasm-install/bin/wasm-validate compiled/repo.wasm 13 | -------------------------------------------------------------------------------- /contract/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "pwasm-repo-contract" 3 | version = "0.1.0" 4 | authors = ["Alexey Frolov "] 5 | 6 | [dependencies] 7 | pwasm-std = "0.10" 8 | pwasm-ethereum = "0.6" 9 | pwasm-abi = "0.1" 10 | pwasm-abi-derive = "0.1" 11 | tiny-keccak = "1.3" 12 | pwasm-token-contract = { git = "https://github.com/paritytech/pwasm-token-example" } 13 | 14 | [dependencies.bigint] 15 | version = "4" 16 | default-features = false 17 | 18 | [dependencies.parity-hash] 19 | version = "1" 20 | default-features = false 21 | 22 | [dev-dependencies] 23 | pwasm-test = { git = "https://github.com/paritytech/pwasm-test" } 24 | 25 | [features] 26 | std = ["pwasm-std/std", "pwasm-ethereum/std", "pwasm-token-contract/std"] 27 | -------------------------------------------------------------------------------- /src/repo.rs: -------------------------------------------------------------------------------- 1 | // Contract doesn't use standard library 2 | #![no_std] 3 | 4 | extern crate pwasm_ethereum; 5 | extern crate pwasm_abi; 6 | extern crate pwasm_repo_contract; 7 | 8 | use pwasm_abi::eth::EndpointInterface; 9 | 10 | /// The main function receives a pointer for the call descriptor. 11 | #[no_mangle] 12 | pub fn call() { 13 | let mut endpoint = pwasm_repo_contract::Endpoint::new(pwasm_repo_contract::RepoContractInstance::new()); 14 | // Read http://solidity.readthedocs.io/en/develop/abi-spec.html#formal-specification-of-the-encoding for details 15 | pwasm_ethereum::ret(&endpoint.dispatch(&pwasm_ethereum::input())); 16 | } 17 | 18 | #[no_mangle] 19 | pub fn deploy() { 20 | let mut endpoint = pwasm_repo_contract::Endpoint::new(pwasm_repo_contract::RepoContractInstance::new()); 21 | endpoint.dispatch_ctor(&pwasm_ethereum::input()); 22 | } 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://travis-ci.org/paritytech/pwasm-token-example.svg?branch=master)](https://travis-ci.org/paritytech/pwasm-repo-contract) 2 | ## Description 3 | A simple contract allows to lend some amount of particular ERC20 token (impl by https://github.com/paritytech/pwasm-token-example) for some interest. It demonstrates how WASM contracts can depend on each other and communicate through ABI. It shows also how to mock callee contract in the test environment. 4 | ## Build prerequisites 5 | Install rust with `wasm32-unknown-unknown` target: 6 | ``` 7 | rustup target add wasm32-unknown-unknown 8 | ``` 9 | Install Wasm build util: 10 | ``` 11 | cargo install pwasm-utils --bin wasm-build 12 | ``` 13 | ## Build 14 | Run: 15 | ``` 16 | ./build.sh 17 | ``` 18 | ## Testing 19 | ``` 20 | cargo test --manifest-path="contract/Cargo.toml" --features std 21 | ``` 22 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Parity Technologies 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /compiled/RepoContract.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "type": "function", 4 | "name": "accept", 5 | "inputs": [], 6 | "outputs": [ 7 | { 8 | "name": "returnValue0", 9 | "type": "bool" 10 | } 11 | ], 12 | "constant": false 13 | }, 14 | { 15 | "type": "function", 16 | "name": "terminate", 17 | "inputs": [], 18 | "outputs": [ 19 | { 20 | "name": "returnValue0", 21 | "type": "bool" 22 | } 23 | ], 24 | "constant": false 25 | }, 26 | { 27 | "type": "function", 28 | "name": "borrower", 29 | "inputs": [], 30 | "outputs": [ 31 | { 32 | "name": "returnValue0", 33 | "type": "address" 34 | } 35 | ], 36 | "constant": true 37 | }, 38 | { 39 | "type": "function", 40 | "name": "lender", 41 | "inputs": [], 42 | "outputs": [ 43 | { 44 | "name": "returnValue0", 45 | "type": "address" 46 | } 47 | ], 48 | "constant": true 49 | }, 50 | { 51 | "type": "function", 52 | "name": "loan_token", 53 | "inputs": [], 54 | "outputs": [ 55 | { 56 | "name": "returnValue0", 57 | "type": "address" 58 | } 59 | ], 60 | "constant": true 61 | }, 62 | { 63 | "type": "function", 64 | "name": "security_token", 65 | "inputs": [], 66 | "outputs": [ 67 | { 68 | "name": "returnValue0", 69 | "type": "address" 70 | } 71 | ], 72 | "constant": true 73 | }, 74 | { 75 | "type": "function", 76 | "name": "loan_amount", 77 | "inputs": [], 78 | "outputs": [ 79 | { 80 | "name": "returnValue0", 81 | "type": "uint256" 82 | } 83 | ], 84 | "constant": true 85 | }, 86 | { 87 | "type": "function", 88 | "name": "security_amount", 89 | "inputs": [], 90 | "outputs": [ 91 | { 92 | "name": "returnValue0", 93 | "type": "uint256" 94 | } 95 | ], 96 | "constant": true 97 | }, 98 | { 99 | "type": "function", 100 | "name": "interest_rate", 101 | "inputs": [], 102 | "outputs": [ 103 | { 104 | "name": "returnValue0", 105 | "type": "uint256" 106 | } 107 | ], 108 | "constant": true 109 | }, 110 | { 111 | "type": "function", 112 | "name": "activation_deadline", 113 | "inputs": [], 114 | "outputs": [ 115 | { 116 | "name": "returnValue0", 117 | "type": "uint64" 118 | } 119 | ], 120 | "constant": true 121 | }, 122 | { 123 | "type": "function", 124 | "name": "return_deadline", 125 | "inputs": [], 126 | "outputs": [ 127 | { 128 | "name": "returnValue0", 129 | "type": "uint64" 130 | } 131 | ], 132 | "constant": true 133 | }, 134 | { 135 | "type": "function", 136 | "name": "borrower_acceptance", 137 | "inputs": [], 138 | "outputs": [ 139 | { 140 | "name": "returnValue0", 141 | "type": "bool" 142 | } 143 | ], 144 | "constant": true 145 | }, 146 | { 147 | "type": "function", 148 | "name": "lender_acceptance", 149 | "inputs": [], 150 | "outputs": [ 151 | { 152 | "name": "returnValue0", 153 | "type": "bool" 154 | } 155 | ], 156 | "constant": true 157 | }, 158 | { 159 | "type": "function", 160 | "name": "is_active", 161 | "inputs": [], 162 | "outputs": [ 163 | { 164 | "name": "returnValue0", 165 | "type": "bool" 166 | } 167 | ], 168 | "constant": true 169 | }, 170 | { 171 | "type": "event", 172 | "name": "LenderAcceptance", 173 | "inputs": [] 174 | }, 175 | { 176 | "type": "event", 177 | "name": "BorrowerAcceptance", 178 | "inputs": [] 179 | }, 180 | { 181 | "type": "event", 182 | "name": "Activation", 183 | "inputs": [] 184 | }, 185 | { 186 | "type": "event", 187 | "name": "Refund", 188 | "inputs": [] 189 | }, 190 | { 191 | "type": "event", 192 | "name": "Default", 193 | "inputs": [] 194 | }, 195 | { 196 | "type": "event", 197 | "name": "Suicide", 198 | "inputs": [] 199 | }, 200 | { 201 | "type": "constructor", 202 | "inputs": [ 203 | { 204 | "name": "borrower", 205 | "type": "address" 206 | }, 207 | { 208 | "name": "lender", 209 | "type": "address" 210 | }, 211 | { 212 | "name": "loan_token", 213 | "type": "address" 214 | }, 215 | { 216 | "name": "security_token", 217 | "type": "address" 218 | }, 219 | { 220 | "name": "loan_amount", 221 | "type": "uint256" 222 | }, 223 | { 224 | "name": "security_amount", 225 | "type": "uint256" 226 | }, 227 | { 228 | "name": "interest_rate", 229 | "type": "uint256" 230 | }, 231 | { 232 | "name": "activation_deadline", 233 | "type": "uint64" 234 | }, 235 | { 236 | "name": "return_deadline", 237 | "type": "uint64" 238 | } 239 | ] 240 | } 241 | ] -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | [[package]] 2 | name = "bigint" 3 | version = "4.4.0" 4 | source = "registry+https://github.com/rust-lang/crates.io-index" 5 | dependencies = [ 6 | "byteorder 1.2.4 (registry+https://github.com/rust-lang/crates.io-index)", 7 | "crunchy 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", 8 | ] 9 | 10 | [[package]] 11 | name = "byteorder" 12 | version = "1.2.4" 13 | source = "registry+https://github.com/rust-lang/crates.io-index" 14 | 15 | [[package]] 16 | name = "cfg-if" 17 | version = "0.1.5" 18 | source = "registry+https://github.com/rust-lang/crates.io-index" 19 | 20 | [[package]] 21 | name = "crunchy" 22 | version = "0.1.6" 23 | source = "registry+https://github.com/rust-lang/crates.io-index" 24 | 25 | [[package]] 26 | name = "dtoa" 27 | version = "0.4.3" 28 | source = "registry+https://github.com/rust-lang/crates.io-index" 29 | 30 | [[package]] 31 | name = "itoa" 32 | version = "0.4.2" 33 | source = "registry+https://github.com/rust-lang/crates.io-index" 34 | 35 | [[package]] 36 | name = "libc" 37 | version = "0.2.43" 38 | source = "registry+https://github.com/rust-lang/crates.io-index" 39 | 40 | [[package]] 41 | name = "memory_units" 42 | version = "0.4.0" 43 | source = "registry+https://github.com/rust-lang/crates.io-index" 44 | 45 | [[package]] 46 | name = "parity-hash" 47 | version = "1.2.0" 48 | source = "registry+https://github.com/rust-lang/crates.io-index" 49 | dependencies = [ 50 | "bigint 4.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 51 | "rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", 52 | ] 53 | 54 | [[package]] 55 | name = "proc-macro2" 56 | version = "0.4.12" 57 | source = "registry+https://github.com/rust-lang/crates.io-index" 58 | dependencies = [ 59 | "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 60 | ] 61 | 62 | [[package]] 63 | name = "pwasm-abi" 64 | version = "0.1.7" 65 | source = "registry+https://github.com/rust-lang/crates.io-index" 66 | dependencies = [ 67 | "bigint 4.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 68 | "byteorder 1.2.4 (registry+https://github.com/rust-lang/crates.io-index)", 69 | "pwasm-std 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", 70 | ] 71 | 72 | [[package]] 73 | name = "pwasm-abi-derive" 74 | version = "0.1.5" 75 | source = "registry+https://github.com/rust-lang/crates.io-index" 76 | dependencies = [ 77 | "byteorder 1.2.4 (registry+https://github.com/rust-lang/crates.io-index)", 78 | "parity-hash 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", 79 | "quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", 80 | "serde 1.0.71 (registry+https://github.com/rust-lang/crates.io-index)", 81 | "serde_derive 1.0.71 (registry+https://github.com/rust-lang/crates.io-index)", 82 | "serde_json 1.0.24 (registry+https://github.com/rust-lang/crates.io-index)", 83 | "syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)", 84 | "tiny-keccak 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)", 85 | ] 86 | 87 | [[package]] 88 | name = "pwasm-alloc" 89 | version = "0.4.0" 90 | source = "registry+https://github.com/rust-lang/crates.io-index" 91 | dependencies = [ 92 | "wee_alloc 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", 93 | ] 94 | 95 | [[package]] 96 | name = "pwasm-ethereum" 97 | version = "0.6.1" 98 | source = "registry+https://github.com/rust-lang/crates.io-index" 99 | dependencies = [ 100 | "bigint 4.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 101 | "byteorder 1.2.4 (registry+https://github.com/rust-lang/crates.io-index)", 102 | "parity-hash 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", 103 | "pwasm-std 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", 104 | ] 105 | 106 | [[package]] 107 | name = "pwasm-libc" 108 | version = "0.2.0" 109 | source = "registry+https://github.com/rust-lang/crates.io-index" 110 | dependencies = [ 111 | "cfg-if 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", 112 | "rlibc 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", 113 | ] 114 | 115 | [[package]] 116 | name = "pwasm-repo-bin" 117 | version = "0.1.0" 118 | dependencies = [ 119 | "pwasm-abi 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", 120 | "pwasm-ethereum 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", 121 | "pwasm-repo-contract 0.1.0", 122 | ] 123 | 124 | [[package]] 125 | name = "pwasm-repo-contract" 126 | version = "0.1.0" 127 | dependencies = [ 128 | "bigint 4.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 129 | "parity-hash 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", 130 | "pwasm-abi 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", 131 | "pwasm-abi-derive 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", 132 | "pwasm-ethereum 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", 133 | "pwasm-std 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", 134 | "pwasm-token-contract 0.1.0 (git+https://github.com/paritytech/pwasm-token-example)", 135 | "tiny-keccak 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)", 136 | ] 137 | 138 | [[package]] 139 | name = "pwasm-std" 140 | version = "0.10.0" 141 | source = "registry+https://github.com/rust-lang/crates.io-index" 142 | dependencies = [ 143 | "byteorder 1.2.4 (registry+https://github.com/rust-lang/crates.io-index)", 144 | "parity-hash 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", 145 | "pwasm-alloc 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 146 | "pwasm-libc 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", 147 | "tiny-keccak 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)", 148 | ] 149 | 150 | [[package]] 151 | name = "pwasm-token-contract" 152 | version = "0.1.0" 153 | source = "git+https://github.com/paritytech/pwasm-token-example#7c447c3d0531123dd288f35d84fa290bc85facf6" 154 | dependencies = [ 155 | "pwasm-abi 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", 156 | "pwasm-abi-derive 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", 157 | "pwasm-ethereum 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", 158 | "pwasm-std 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", 159 | "tiny-keccak 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)", 160 | ] 161 | 162 | [[package]] 163 | name = "quote" 164 | version = "0.3.15" 165 | source = "registry+https://github.com/rust-lang/crates.io-index" 166 | 167 | [[package]] 168 | name = "quote" 169 | version = "0.6.6" 170 | source = "registry+https://github.com/rust-lang/crates.io-index" 171 | dependencies = [ 172 | "proc-macro2 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", 173 | ] 174 | 175 | [[package]] 176 | name = "rlibc" 177 | version = "1.0.0" 178 | source = "registry+https://github.com/rust-lang/crates.io-index" 179 | 180 | [[package]] 181 | name = "rustc-hex" 182 | version = "1.0.0" 183 | source = "registry+https://github.com/rust-lang/crates.io-index" 184 | 185 | [[package]] 186 | name = "serde" 187 | version = "1.0.71" 188 | source = "registry+https://github.com/rust-lang/crates.io-index" 189 | 190 | [[package]] 191 | name = "serde_derive" 192 | version = "1.0.71" 193 | source = "registry+https://github.com/rust-lang/crates.io-index" 194 | dependencies = [ 195 | "proc-macro2 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", 196 | "quote 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)", 197 | "syn 0.14.8 (registry+https://github.com/rust-lang/crates.io-index)", 198 | ] 199 | 200 | [[package]] 201 | name = "serde_json" 202 | version = "1.0.24" 203 | source = "registry+https://github.com/rust-lang/crates.io-index" 204 | dependencies = [ 205 | "dtoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", 206 | "itoa 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", 207 | "serde 1.0.71 (registry+https://github.com/rust-lang/crates.io-index)", 208 | ] 209 | 210 | [[package]] 211 | name = "syn" 212 | version = "0.11.11" 213 | source = "registry+https://github.com/rust-lang/crates.io-index" 214 | dependencies = [ 215 | "quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", 216 | "synom 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)", 217 | "unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)", 218 | ] 219 | 220 | [[package]] 221 | name = "syn" 222 | version = "0.14.8" 223 | source = "registry+https://github.com/rust-lang/crates.io-index" 224 | dependencies = [ 225 | "proc-macro2 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", 226 | "quote 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)", 227 | "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 228 | ] 229 | 230 | [[package]] 231 | name = "synom" 232 | version = "0.11.3" 233 | source = "registry+https://github.com/rust-lang/crates.io-index" 234 | dependencies = [ 235 | "unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)", 236 | ] 237 | 238 | [[package]] 239 | name = "tiny-keccak" 240 | version = "1.4.2" 241 | source = "registry+https://github.com/rust-lang/crates.io-index" 242 | dependencies = [ 243 | "crunchy 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", 244 | ] 245 | 246 | [[package]] 247 | name = "unicode-xid" 248 | version = "0.0.4" 249 | source = "registry+https://github.com/rust-lang/crates.io-index" 250 | 251 | [[package]] 252 | name = "unicode-xid" 253 | version = "0.1.0" 254 | source = "registry+https://github.com/rust-lang/crates.io-index" 255 | 256 | [[package]] 257 | name = "unreachable" 258 | version = "1.0.0" 259 | source = "registry+https://github.com/rust-lang/crates.io-index" 260 | dependencies = [ 261 | "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", 262 | ] 263 | 264 | [[package]] 265 | name = "void" 266 | version = "1.0.2" 267 | source = "registry+https://github.com/rust-lang/crates.io-index" 268 | 269 | [[package]] 270 | name = "wee_alloc" 271 | version = "0.4.2" 272 | source = "registry+https://github.com/rust-lang/crates.io-index" 273 | dependencies = [ 274 | "cfg-if 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", 275 | "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", 276 | "memory_units 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 277 | "unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", 278 | "winapi 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", 279 | ] 280 | 281 | [[package]] 282 | name = "winapi" 283 | version = "0.3.5" 284 | source = "registry+https://github.com/rust-lang/crates.io-index" 285 | dependencies = [ 286 | "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 287 | "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 288 | ] 289 | 290 | [[package]] 291 | name = "winapi-i686-pc-windows-gnu" 292 | version = "0.4.0" 293 | source = "registry+https://github.com/rust-lang/crates.io-index" 294 | 295 | [[package]] 296 | name = "winapi-x86_64-pc-windows-gnu" 297 | version = "0.4.0" 298 | source = "registry+https://github.com/rust-lang/crates.io-index" 299 | 300 | [metadata] 301 | "checksum bigint 4.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "da1dde4308822ffaa13665757273a1b787481212f3f9b1c470a864b179a01f1b" 302 | "checksum byteorder 1.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8389c509ec62b9fe8eca58c502a0acaf017737355615243496cde4994f8fa4f9" 303 | "checksum cfg-if 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "0c4e7bb64a8ebb0d856483e1e682ea3422f883c5f5615a90d51a2c82fe87fdd3" 304 | "checksum crunchy 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "a2f4a431c5c9f662e1200b7c7f02c34e91361150e382089a8f2dec3ba680cbda" 305 | "checksum dtoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6d301140eb411af13d3115f9a562c85cc6b541ade9dfa314132244aaee7489dd" 306 | "checksum itoa 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "5adb58558dcd1d786b5f0bd15f3226ee23486e24b7b58304b60f64dc68e62606" 307 | "checksum libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)" = "76e3a3ef172f1a0b9a9ff0dd1491ae5e6c948b94479a3021819ba7d860c8645d" 308 | "checksum memory_units 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8452105ba047068f40ff7093dd1d9da90898e63dd61736462e9cdda6a90ad3c3" 309 | "checksum parity-hash 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ad090011741dd5c6b7aee2e08717b9eecb2f5ccf1e0345533015cef152c8b020" 310 | "checksum proc-macro2 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)" = "7a17a4d77bc20d344179de803a34694c0ac7a0b3fb4384bee99783215a8e0410" 311 | "checksum pwasm-abi 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "a0c9d01c5207ffc9c2864624be18d259fc1b4d0b18ec44f639acd74aa9c7e877" 312 | "checksum pwasm-abi-derive 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "142899de0e942e8c8f2da34bed8bf9c4751b4ef07028aa2e07bc9c3206dd6e41" 313 | "checksum pwasm-alloc 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bf268e76f224753679a7cb6e0261c9226676c83cd4d2b6ead2095524f00c854f" 314 | "checksum pwasm-ethereum 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "127d50a63437b74313d23f889fa2e4782712815da191b863264a5c038191b601" 315 | "checksum pwasm-libc 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "027307d6d2086ffb7f399227d42593c5341e8869a55ac8dd7c79d7ca04ec00e2" 316 | "checksum pwasm-std 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "813c55a13acb1e369ca3f75817fdb913c1765e6219b4a4331e89765c01f41670" 317 | "checksum pwasm-token-contract 0.1.0 (git+https://github.com/paritytech/pwasm-token-example)" = "" 318 | "checksum quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e920b65c65f10b2ae65c831a81a073a89edd28c7cce89475bff467ab4167a" 319 | "checksum quote 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)" = "ed7d650913520df631972f21e104a4fa2f9c82a14afc65d17b388a2e29731e7c" 320 | "checksum rlibc 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc874b127765f014d792f16763a81245ab80500e2ad921ed4ee9e82481ee08fe" 321 | "checksum rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0ceb8ce7a5e520de349e1fa172baeba4a9e8d5ef06c47471863530bc4972ee1e" 322 | "checksum serde 1.0.71 (registry+https://github.com/rust-lang/crates.io-index)" = "6dfad05c8854584e5f72fb859385ecdfa03af69c3fd0572f0da2d4c95f060bdb" 323 | "checksum serde_derive 1.0.71 (registry+https://github.com/rust-lang/crates.io-index)" = "b719c6d5e9f73fbc37892246d5852333f040caa617b8873c6aced84bcb28e7bb" 324 | "checksum serde_json 1.0.24 (registry+https://github.com/rust-lang/crates.io-index)" = "c3c6908c7b925cd6c590358a4034de93dbddb20c45e1d021931459fd419bf0e2" 325 | "checksum syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)" = "d3b891b9015c88c576343b9b3e41c2c11a51c219ef067b264bd9c8aa9b441dad" 326 | "checksum syn 0.14.8 (registry+https://github.com/rust-lang/crates.io-index)" = "b7bfcbb0c068d0f642a0ffbd5c604965a360a61f99e8add013cef23a838614f3" 327 | "checksum synom 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a393066ed9010ebaed60b9eafa373d4b1baac186dd7e008555b0f702b51945b6" 328 | "checksum tiny-keccak 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e9175261fbdb60781fcd388a4d6cc7e14764a2b629a7ad94abb439aed223a44f" 329 | "checksum unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f860d7d29cf02cb2f3f359fd35991af3d30bac52c57d265a3c461074cb4dc" 330 | "checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" 331 | "checksum unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "382810877fe448991dfc7f0dd6e3ae5d58088fd0ea5e35189655f84e6814fa56" 332 | "checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" 333 | "checksum wee_alloc 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "27875be1daf838fa18f3e94fd19fd12638e34615b42f56da2610c8f46be80cc6" 334 | "checksum winapi 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "773ef9dcc5f24b7d850d0ff101e542ff24c3b090a9768e03ff889fdef41f00fd" 335 | "checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 336 | "checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 337 | -------------------------------------------------------------------------------- /contract/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![cfg_attr(not(feature="std"), no_main)] 2 | #![cfg_attr(not(feature="std"), no_std)] 3 | 4 | #![feature(use_extern_macros)] 5 | #![feature(proc_macro_gen)] 6 | #![allow(non_snake_case)] 7 | #![feature(alloc)] 8 | 9 | extern crate alloc; 10 | extern crate bigint; 11 | extern crate parity_hash; 12 | extern crate pwasm_std; 13 | extern crate pwasm_ethereum; 14 | extern crate pwasm_abi; 15 | extern crate pwasm_abi_derive; 16 | extern crate pwasm_token_contract; 17 | 18 | use pwasm_std::hash::{Address, H256}; 19 | use bigint::U256; 20 | use pwasm_abi_derive::eth_abi; 21 | 22 | use pwasm_token_contract::TokenContract; 23 | use pwasm_token_contract::Client as Token; 24 | 25 | use pwasm_ethereum as eth; 26 | use pwasm_std::Vec; 27 | 28 | // Generates storage keys. Each key = previous_key + 1. 256 keys max 29 | macro_rules! storage_keys { 30 | () => {}; 31 | ($($name:ident),*) => { 32 | storage_keys!(0u8, $($name),*); 33 | }; 34 | ($count:expr, $name:ident) => { 35 | static $name: H256 = H256([$count, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]); 36 | }; 37 | ($count:expr, $name:ident, $($tail:ident),*) => { 38 | static $name: H256 = H256([$count, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]); 39 | storage_keys!($count + 1u8, $($tail),*); 40 | }; 41 | } 42 | 43 | struct Entry { 44 | key: H256, 45 | value: [u8; 32] 46 | } 47 | 48 | struct Storage { 49 | table: Vec, 50 | } 51 | 52 | /// TODO: optimise by impl hashtable 53 | impl Storage { 54 | fn with_capacity(cap: usize) -> Storage { 55 | Storage { 56 | table: Vec::with_capacity(cap) 57 | } 58 | } 59 | 60 | fn read(&mut self, key: &H256) -> [u8; 32] { 61 | // First: lookup in the table 62 | for entry in &self.table { 63 | if *key == entry.key { 64 | return entry.value.clone(); 65 | } 66 | } 67 | // Second: read from the storage 68 | let value = eth::read(key); 69 | self.table.push(Entry { 70 | key: key.clone(), 71 | value: value.clone() 72 | }); 73 | value 74 | } 75 | 76 | fn write(&mut self, key: &H256, value: &[u8; 32]) { 77 | eth::write(key, value); 78 | for entry in &mut self.table { 79 | if *key == entry.key { 80 | entry.value = *value; 81 | return; 82 | } 83 | } 84 | self.table.push(Entry { 85 | key: key.clone(), 86 | value: value.clone() 87 | }); 88 | } 89 | } 90 | 91 | #[eth_abi(Endpoint, Client)] 92 | pub trait RepoContract { 93 | fn constructor(&mut self, 94 | borrower: Address, 95 | lender: Address, 96 | loan_token: Address, 97 | security_token: Address, 98 | loan_amount: U256, 99 | security_amount: U256, 100 | interest_rate: U256, 101 | activation_deadline: u64, 102 | return_deadline: u64); 103 | fn accept(&mut self) -> bool; 104 | fn terminate(&mut self) -> bool; 105 | #[constant] 106 | fn borrower(&mut self) -> Address; 107 | #[constant] 108 | fn lender(&mut self) -> Address; 109 | #[constant] 110 | fn loan_token(&mut self) -> Address; 111 | #[constant] 112 | fn security_token(&mut self) -> Address; 113 | #[constant] 114 | fn loan_amount(&mut self) -> U256; 115 | #[constant] 116 | fn security_amount(&mut self) -> U256; 117 | #[constant] 118 | fn interest_rate(&mut self) -> U256; 119 | #[constant] 120 | fn activation_deadline(&mut self) -> u64; 121 | #[constant] 122 | fn return_deadline(&mut self) -> u64; 123 | #[constant] 124 | fn borrower_acceptance(&mut self) -> bool; 125 | #[constant] 126 | fn lender_acceptance(&mut self) -> bool; 127 | #[constant] 128 | fn is_active(&mut self) -> bool; 129 | #[event] 130 | fn LenderAcceptance(&mut self); 131 | #[event] 132 | fn BorrowerAcceptance(&mut self); 133 | #[event] 134 | fn Activation(&mut self); 135 | #[event] 136 | fn Refund(&mut self); 137 | #[event] 138 | fn Default(&mut self); 139 | #[event] 140 | fn Suicide(&mut self); 141 | } 142 | 143 | storage_keys!( 144 | BORROWER_KEY, LENDER_KEY, 145 | LOAN_TOKEN_KEY, SECURITY_TOKEN_KEY, 146 | LOAN_AMOUNT_KEY, SECURITY_AMOUNT_KEY, 147 | INTEREST_RATE_KEY, 148 | ACTIVATION_DEADLINE_KEY, RETURN_DEADLINE_KEY, 149 | BORROW_ACCEPTED_KEY, LEND_ACCEPTED_KEY 150 | ); 151 | 152 | static DIVISOR: U256 = U256([100,0,0,0]); 153 | 154 | pub struct RepoContractInstance { 155 | storage: Storage 156 | } 157 | 158 | impl RepoContractInstance { 159 | pub fn new() -> RepoContractInstance { 160 | RepoContractInstance { 161 | storage: Storage::with_capacity(10) 162 | } 163 | } 164 | 165 | pub fn suicide(&mut self, sender: &Address) -> bool { 166 | self.Suicide(); 167 | if cfg!(test) { 168 | return false 169 | } 170 | eth::suicide(sender); 171 | } 172 | } 173 | 174 | impl RepoContract for RepoContractInstance { 175 | /// A contract constructor implementation. 176 | fn constructor(&mut self, 177 | borrower: Address, 178 | lender: Address, 179 | loan_token: Address, 180 | security_token: Address, 181 | loan_amount: U256, 182 | security_amount: U256, 183 | interest_rate: U256, 184 | activation_deadline: u64, 185 | return_deadline: u64 186 | ) { 187 | self.storage.write(&BORROWER_KEY, &H256::from(borrower).into()); 188 | self.storage.write(&LENDER_KEY, &H256::from(lender).into()); 189 | self.storage.write(&LOAN_TOKEN_KEY, &H256::from(loan_token).into()); 190 | self.storage.write(&SECURITY_TOKEN_KEY, &H256::from(security_token).into()); 191 | self.storage.write(&LOAN_AMOUNT_KEY, &loan_amount.into()); 192 | self.storage.write(&SECURITY_AMOUNT_KEY, &security_amount.into()); 193 | self.storage.write(&INTEREST_RATE_KEY, &interest_rate.into()); 194 | self.storage.write(&ACTIVATION_DEADLINE_KEY, &U256::from(activation_deadline).into()); 195 | self.storage.write(&RETURN_DEADLINE_KEY, &U256::from(return_deadline).into()); 196 | } 197 | 198 | fn borrower(&mut self) -> Address { 199 | H256::from(self.storage.read(&BORROWER_KEY)).into() 200 | } 201 | 202 | fn lender(&mut self) -> Address { 203 | H256::from(self.storage.read(&LENDER_KEY)).into() 204 | } 205 | 206 | fn loan_token(&mut self) -> Address { 207 | H256::from(self.storage.read(&LOAN_TOKEN_KEY)).into() 208 | } 209 | 210 | fn security_token(&mut self) -> Address { 211 | H256::from(self.storage.read(&SECURITY_TOKEN_KEY)).into() 212 | } 213 | 214 | fn loan_amount(&mut self) -> U256 { 215 | self.storage.read(&LOAN_AMOUNT_KEY).into() 216 | } 217 | 218 | fn security_amount(&mut self) -> U256 { 219 | self.storage.read(&SECURITY_AMOUNT_KEY).into() 220 | } 221 | 222 | fn interest_rate(&mut self) -> U256 { 223 | self.storage.read(&INTEREST_RATE_KEY).into() 224 | } 225 | 226 | fn activation_deadline(&mut self) -> u64 { 227 | U256::from(self.storage.read(&ACTIVATION_DEADLINE_KEY)).into() 228 | } 229 | 230 | fn return_deadline(&mut self) -> u64 { 231 | U256::from(self.storage.read(&RETURN_DEADLINE_KEY)).into() 232 | } 233 | 234 | fn borrower_acceptance(&mut self) -> bool { 235 | let value = U256::from(self.storage.read(&BORROW_ACCEPTED_KEY)); 236 | if value == 0.into() { 237 | false 238 | } else { 239 | true 240 | } 241 | } 242 | 243 | fn lender_acceptance(&mut self) -> bool { 244 | let value = U256::from(self.storage.read(&LEND_ACCEPTED_KEY)); 245 | if value == 0.into() { 246 | false 247 | } else { 248 | true 249 | } 250 | } 251 | 252 | fn is_active(&mut self) -> bool { 253 | self.borrower_acceptance() && self.lender_acceptance() 254 | } 255 | 256 | // Tries to activate contract 257 | fn accept(&mut self) -> bool { 258 | let sender = eth::sender(); 259 | if self.is_active() { 260 | panic!("Cannot accept, contract has activated already"); 261 | } 262 | if eth::timestamp() > self.activation_deadline() { 263 | self.suicide(&sender); 264 | } 265 | let lender_address = self.lender(); 266 | let borrower_address = self.borrower(); 267 | 268 | // Accept by borrower 269 | if sender == borrower_address { 270 | self.storage.write(&BORROW_ACCEPTED_KEY, &U256::from(1).into()); 271 | self.BorrowerAcceptance(); 272 | } 273 | // Accept by lender 274 | else if sender == lender_address { 275 | self.storage.write(&LEND_ACCEPTED_KEY, &U256::from(1).into()); 276 | self.LenderAcceptance(); 277 | } else { 278 | panic!("Only for participants"); 279 | } 280 | 281 | // Wait for all parties to accept 282 | if !(self.borrower_acceptance() && self.lender_acceptance()) { 283 | return false; 284 | } 285 | 286 | let mut loan_token = Token::new(self.loan_token()).gas(100000); 287 | let mut security_token = Token::new(self.security_token()).gas(100000); 288 | let loan_amount = self.loan_amount(); 289 | let security_amount = self.security_amount(); 290 | 291 | let this_contract_address = eth::address(); 292 | // Transfer security from borrower_address to the contract address 293 | assert!(security_token.transferFrom(borrower_address, this_contract_address, security_amount)); 294 | // Transfer loan to the borrower address 295 | assert!(loan_token.transferFrom(lender_address, borrower_address, loan_amount)); 296 | 297 | return true; 298 | } 299 | 300 | fn terminate(&mut self) -> bool { 301 | let sender = eth::sender(); 302 | if !self.is_active() && eth::timestamp() > self.activation_deadline() { 303 | self.suicide(&sender); 304 | } 305 | let lender_address = self.lender(); 306 | let borrower_address = self.borrower(); 307 | let mut loan_token = Token::new(self.loan_token()).gas(100000); 308 | let mut security_token = Token::new(self.security_token()).gas(100000); 309 | let loan_amount = self.loan_amount(); 310 | let security_amount = self.security_amount(); 311 | let interest_amount = (loan_amount / DIVISOR) * self.interest_rate(); 312 | let return_amount = loan_amount + interest_amount; 313 | 314 | if eth::timestamp() <= self.return_deadline() { 315 | if sender != borrower_address { 316 | panic!("Only borrower can terminate contract if deadline hasn't came"); 317 | } 318 | assert!(loan_token.transferFrom(borrower_address, lender_address, return_amount)); 319 | assert!(security_token.transfer(borrower_address, security_amount)); 320 | self.Refund(); 321 | self.suicide(&sender) 322 | } else { 323 | assert!(security_token.transfer(lender_address, security_amount)); 324 | self.Default(); 325 | self.suicide(&sender) 326 | } 327 | } 328 | } 329 | 330 | #[cfg(test)] 331 | extern crate pwasm_test; 332 | 333 | #[cfg(test)] 334 | #[allow(non_snake_case)] 335 | mod tests { 336 | extern crate std; 337 | extern crate pwasm_token_contract; 338 | 339 | use pwasm_test; 340 | use super::*; 341 | use self::pwasm_test::{ext_get, ext_update, ext_reset}; 342 | use bigint::U256; 343 | use pwasm_std::hash::{Address, H160, H256}; 344 | 345 | // Can't just alias Address for tuple struct initialization. Seems like a compiller bug 346 | static BORROWER_ADDR: Address = H160([ 347 | 0xea, 0x67, 0x4f, 0xdd, 0xe7, 0x14, 0xfd, 0x97, 0x9d, 0xe3, 348 | 0xed, 0xf0, 0xf5, 0x6a, 0xa9, 0x71, 0x6b, 0x89, 0x8e, 0xc8]); 349 | static LENDER_ADDR: Address = H160([ 350 | 0xdb, 0x6f, 0xd4, 0x84, 0xcf, 0xa4, 0x6e, 0xee, 0xb7, 0x3c, 351 | 0x71, 0xed, 0xee, 0x82, 0x3e, 0x48, 0x12, 0xf9, 0xe2, 0xe1 352 | ]); 353 | static LOAN_TOKEN_ADDR: Address = H160([ 354 | 0x0f, 0x57, 0x2e, 0x52, 0x95, 0xc5, 0x7f, 0x15, 0x88, 0x6f, 355 | 0x9b, 0x26, 0x3e, 0x2f, 0x6d, 0x2d, 0x6c, 0x7b, 0x5e, 0xc6 356 | ]); 357 | static SECURITY_TOKEN_ADDR: Address = H160([ 358 | 0xcd, 0x17, 0x22, 0xf2, 0x94, 0x7d, 0xef, 0x4c, 0xf1, 0x44, 359 | 0x67, 0x9d, 0xa3, 0x9c, 0x4c, 0x32, 0xbd, 0xc3, 0x56, 0x81 360 | ]); 361 | static CONTRACT_ADDR: Address = H160([ 362 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 363 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]); 364 | 365 | use self::pwasm_token_contract::{TokenContract, Endpoint as TokenEndpoint}; 366 | 367 | #[derive(Default)] 368 | struct TokenMock { 369 | balanceOf: U256, 370 | totalSupply: U256, 371 | transfer: bool, 372 | approve: bool, 373 | allowance: U256, 374 | transferFrom: bool, 375 | } 376 | 377 | impl TokenMock { 378 | fn with_transfer(mut self, transfer: bool) -> TokenMock { 379 | self.transfer = transfer; 380 | self 381 | } 382 | fn with_transfer_from(mut self, transfer_from: bool) -> TokenMock { 383 | self.transferFrom = transfer_from; 384 | self 385 | } 386 | } 387 | 388 | impl TokenContract for TokenMock { 389 | fn constructor(&mut self, _total_supply: U256) { 390 | } 391 | fn balanceOf(&mut self, _owner: Address) -> U256 { 392 | self.balanceOf 393 | } 394 | fn totalSupply(&mut self) -> U256 { 395 | self.totalSupply 396 | } 397 | fn transfer(&mut self, _to: Address, _amount: U256) -> bool { 398 | self.transfer 399 | } 400 | fn approve(&mut self, _spender: Address, _value: U256) -> bool { 401 | self.approve 402 | } 403 | fn allowance(&mut self, _owner: Address, _spender: Address) -> U256 { 404 | self.allowance 405 | } 406 | fn transferFrom(&mut self, _from: Address, _to: Address, _amount: U256) -> bool { 407 | self.transferFrom 408 | } 409 | } 410 | 411 | fn default_contract() -> RepoContractInstance { 412 | let mut contract = RepoContractInstance::new(); 413 | let loan_amount: U256 = 10000.into(); 414 | let security_amount: U256 = 50000.into(); 415 | let interest_rate: U256 = 3.into(); 416 | let activation_deadline: u64 = 10; 417 | let return_deadline: u64 = 20; 418 | contract.constructor(BORROWER_ADDR.clone(), 419 | LENDER_ADDR.clone(), 420 | LOAN_TOKEN_ADDR.clone(), 421 | SECURITY_TOKEN_ADDR.clone(), 422 | loan_amount, 423 | security_amount, 424 | interest_rate, 425 | activation_deadline, 426 | return_deadline); 427 | contract 428 | } 429 | 430 | fn active_contract() -> RepoContractInstance { 431 | ext_reset(|e| e 432 | .sender(BORROWER_ADDR) 433 | .timestamp(5) 434 | .endpoint(LOAN_TOKEN_ADDR, TokenEndpoint::new(TokenMock::default().with_transfer_from(true)).into()) 435 | .endpoint(SECURITY_TOKEN_ADDR, TokenEndpoint::new(TokenMock::default().with_transfer_from(true)).into()) 436 | ); 437 | let mut contract = default_contract(); 438 | contract.accept(); 439 | ext_update(|e| e.sender(LENDER_ADDR)); 440 | contract.accept(); 441 | contract 442 | } 443 | 444 | #[test] 445 | fn should_create_contract_with_storage () { 446 | ext_reset(|e| e); 447 | let mut contract = RepoContractInstance::new(); 448 | let loan_amount: U256 = 10000.into(); 449 | let security_amount: U256 = 50000.into(); 450 | let interest_rate: U256 = 3.into(); 451 | let activation_deadline: u64 = 10; 452 | let return_deadline: u64 = 20; 453 | 454 | contract.constructor(BORROWER_ADDR.clone(), 455 | LENDER_ADDR.clone(), 456 | LOAN_TOKEN_ADDR.clone(), 457 | SECURITY_TOKEN_ADDR.clone(), 458 | loan_amount, 459 | security_amount, 460 | interest_rate, 461 | activation_deadline, 462 | return_deadline); 463 | 464 | assert_eq!(contract.borrower(), BORROWER_ADDR); 465 | assert_eq!(contract.lender(), LENDER_ADDR); 466 | assert_eq!(contract.loan_token(), LOAN_TOKEN_ADDR); 467 | assert_eq!(contract.security_token(), SECURITY_TOKEN_ADDR); 468 | assert_eq!(contract.loan_amount(), loan_amount); 469 | assert_eq!(contract.security_amount(), security_amount); 470 | assert_eq!(contract.interest_rate(), interest_rate); 471 | assert_eq!(contract.activation_deadline(), activation_deadline); 472 | assert_eq!(contract.return_deadline(), return_deadline); 473 | } 474 | 475 | #[test] 476 | fn should_activate_contract () { 477 | ext_reset(|e| e 478 | .sender(BORROWER_ADDR) 479 | .timestamp(5) 480 | .endpoint(LOAN_TOKEN_ADDR, TokenEndpoint::new(TokenMock::default().with_transfer_from(true)).into()) 481 | .endpoint(SECURITY_TOKEN_ADDR, TokenEndpoint::new(TokenMock::default().with_transfer_from(true)).into()) 482 | ); 483 | 484 | let mut contract = default_contract(); 485 | assert_eq!(contract.accept(), false); 486 | assert_eq!(contract.borrower_acceptance(), true); 487 | // Set sender to lender 488 | ext_update(|e| e.sender(LENDER_ADDR)); 489 | assert_eq!(contract.accept(), true); 490 | let ext_calls = ext_get().calls(); 491 | assert_eq!(ext_calls.len(), 2, "2 transfer calls expected"); 492 | let security_transfer = &ext_calls[0]; 493 | let loan_transfer = &ext_calls[1]; 494 | assert_eq!(security_transfer.address, SECURITY_TOKEN_ADDR); 495 | assert_eq!(loan_transfer.address, LOAN_TOKEN_ADDR); 496 | 497 | // Check transfers 498 | assert_eq!(Address::from(H256::from(&security_transfer.input[4..36])), BORROWER_ADDR); 499 | assert_eq!(Address::from(H256::from(&security_transfer.input[36..68])), CONTRACT_ADDR); 500 | assert_eq!(U256::from(H256::from(&security_transfer.input[68..100])), 50000.into()); 501 | 502 | assert_eq!(Address::from(H256::from(&loan_transfer.input[4..36])), LENDER_ADDR); 503 | assert_eq!(Address::from(H256::from(&loan_transfer.input[36..68])), BORROWER_ADDR); 504 | assert_eq!(U256::from(H256::from(&loan_transfer.input[68..100])), 10000.into()); 505 | 506 | assert_eq!(contract.lender_acceptance(), true); 507 | assert_eq!(contract.is_active(), true); 508 | } 509 | 510 | #[test] 511 | #[should_panic] 512 | fn should_panic_if_contract_cant_transfer_loan_token() { 513 | ext_reset(|e| e 514 | .sender(BORROWER_ADDR) 515 | .timestamp(5) 516 | .endpoint(LOAN_TOKEN_ADDR, TokenEndpoint::new(TokenMock::default().with_transfer_from(false)).into()) 517 | .endpoint(SECURITY_TOKEN_ADDR, TokenEndpoint::new(TokenMock::default().with_transfer_from(true)).into()) 518 | ); 519 | 520 | let mut contract = default_contract(); 521 | assert_eq!(contract.accept(), false); 522 | // Set sender to lender 523 | ext_reset(|e| e.sender(LENDER_ADDR)); 524 | // Should panic because contact can't transfer amount of LOAN_TOKEN for some reason 525 | contract.accept(); 526 | } 527 | 528 | #[test] 529 | #[should_panic] 530 | fn should_panic_if_contract_cant_transfer_security_token() { 531 | ext_reset(|e| e 532 | .sender(BORROWER_ADDR) 533 | .timestamp(5) 534 | .endpoint(LOAN_TOKEN_ADDR, TokenEndpoint::new(TokenMock::default().with_transfer_from(true)).into()) 535 | .endpoint(SECURITY_TOKEN_ADDR, TokenEndpoint::new(TokenMock::default().with_transfer_from(false)).into()) 536 | ); 537 | 538 | let mut contract = default_contract(); 539 | assert_eq!(contract.accept(), false); 540 | // Set sender to lender 541 | ext_update(|e| e.sender(LENDER_ADDR)); 542 | // Should panic because contact can't transfer amount of LOAN_TOKEN for some reason 543 | contract.accept(); 544 | } 545 | 546 | #[test] 547 | fn should_suicide_if_activation_deadline_came() { 548 | let mut contract = default_contract(); 549 | ext_reset(|e| e.sender(BORROWER_ADDR).timestamp(11)); 550 | assert_eq!(contract.accept(), false); 551 | } 552 | 553 | // Active contract tests 554 | #[test] 555 | #[should_panic] 556 | fn should_panic_if_terminate_before_accept() { 557 | let mut contract = default_contract(); 558 | ext_reset(|e| e.sender(BORROWER_ADDR).timestamp(5)); 559 | contract.terminate(); 560 | } 561 | 562 | #[test] 563 | fn should_terminate_by_borrower() { 564 | let mut contract = active_contract(); 565 | ext_reset(|e| e 566 | .sender(BORROWER_ADDR) 567 | .timestamp(15) 568 | .endpoint(LOAN_TOKEN_ADDR, TokenEndpoint::new(TokenMock::default().with_transfer_from(true)).into()) 569 | .endpoint(SECURITY_TOKEN_ADDR, TokenEndpoint::new(TokenMock::default().with_transfer(true)).into()) 570 | ); 571 | 572 | contract.terminate(); 573 | let ext_calls = ext_get().calls(); 574 | assert_eq!(ext_calls.len(), 2, "2 transfer calls expected"); 575 | 576 | let loan_transfer = &ext_calls[0]; 577 | let security_transfer = &ext_calls[1]; 578 | assert_eq!(security_transfer.address, SECURITY_TOKEN_ADDR); 579 | assert_eq!(loan_transfer.address, LOAN_TOKEN_ADDR); 580 | 581 | // Check transfers 582 | assert_eq!(Address::from(H256::from(&security_transfer.input[4..36])), BORROWER_ADDR); 583 | assert_eq!(U256::from(H256::from(&security_transfer.input[36..68])), 50000.into()); 584 | 585 | assert_eq!(Address::from(H256::from(&loan_transfer.input[4..36])), BORROWER_ADDR); 586 | assert_eq!(Address::from(H256::from(&loan_transfer.input[36..68])), LENDER_ADDR); 587 | assert_eq!(U256::from(H256::from(&loan_transfer.input[68..100])), 10300.into()); // 10000 + 300 of interest 588 | } 589 | 590 | #[test] 591 | #[should_panic] 592 | fn should_not_be_able_to_terminate_by_anybody_exept_borrower_if_deadline_hasnt_came() { 593 | let mut contract = active_contract(); 594 | ext_reset(|e| e.sender(LENDER_ADDR).timestamp(15)); 595 | contract.terminate(); 596 | } 597 | 598 | #[test] 599 | fn can_be_terminated_if_deadline() { 600 | let mut contract = active_contract(); 601 | ext_reset(|e| e 602 | .sender(Address::new()) // by anybody 603 | .timestamp(25) 604 | .endpoint(SECURITY_TOKEN_ADDR, TokenEndpoint::new(TokenMock::default().with_transfer(true)).into()) 605 | ); 606 | 607 | contract.terminate(); 608 | 609 | let ext_calls = ext_get().calls(); 610 | assert_eq!(ext_calls.len(), 1, "1 transfer call expected"); 611 | let security_transfer = &ext_calls[0]; 612 | assert_eq!(security_transfer.address, SECURITY_TOKEN_ADDR); 613 | // Check transfers 614 | assert_eq!(Address::from(H256::from(&security_transfer.input[4..36])), LENDER_ADDR); 615 | assert_eq!(U256::from(H256::from(&security_transfer.input[36..68])), 50000.into()); 616 | } 617 | } 618 | --------------------------------------------------------------------------------