├── .babelrc ├── .eslintignore ├── .eslintrc.js ├── .flowconfig ├── .gitignore ├── .prettierrc.yaml ├── .travis.yml ├── LICENSE ├── README.md ├── cluster-devnet.env ├── cluster-mainnet-beta.env ├── cluster-testnet.env ├── flow-typed ├── bn.js.js ├── bs58.js ├── buffer-layout.js ├── cbor.js ├── event-emitter.js ├── json-to-pretty-yaml.js ├── mkdirp-promise_vx.x.x.js ├── npm │ └── mz_vx.x.x.js ├── readline-promise.js └── semver.js ├── package-lock.json ├── package.json ├── src ├── cli │ ├── main.js │ └── token-test.js ├── client │ ├── layout.js │ ├── token.js │ └── util │ │ ├── new-account-with-lamports.js │ │ ├── new-system-account-with-airdrop.js │ │ ├── send-and-confirm-transaction.js │ │ └── sleep.js ├── program-test │ ├── .gitignore │ ├── Cargo.lock │ ├── Cargo.toml │ └── tests │ │ └── bench.rs └── program │ ├── .gitignore │ ├── Cargo.lock │ ├── Cargo.toml │ ├── README.md │ ├── Xargo.toml │ ├── do.sh │ └── src │ ├── error.rs │ ├── lib.rs │ ├── processor.rs │ └── state.rs └── url.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | "env", 4 | "flow", 5 | "react", 6 | "stage-2", 7 | ], 8 | "plugins": [ 9 | "transform-class-properties", 10 | "transform-function-bind", 11 | "transform-runtime", 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | dist/ 2 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | // eslint-disable-line import/no-commonjs 3 | env: { 4 | browser: true, 5 | es6: true, 6 | node: true, 7 | }, 8 | plugins: ['react'], 9 | extends: [ 10 | 'eslint:recommended', 11 | 'plugin:import/errors', 12 | 'plugin:import/warnings', 13 | 'plugin:react/recommended', 14 | ], 15 | parser: 'babel-eslint', 16 | parserOptions: { 17 | sourceType: 'module', 18 | ecmaVersion: 8, 19 | }, 20 | rules: { 21 | 'no-trailing-spaces': ['error'], 22 | 'import/first': ['error'], 23 | 'import/no-commonjs': ['error'], 24 | 'import/order': [ 25 | 'error', 26 | { 27 | groups: [ 28 | ['internal', 'external', 'builtin'], 29 | ['index', 'sibling', 'parent'], 30 | ], 31 | 'newlines-between': 'always', 32 | }, 33 | ], 34 | indent: [ 35 | 'error', 36 | 2, 37 | { 38 | MemberExpression: 1, 39 | SwitchCase: 1, 40 | }, 41 | ], 42 | 'linebreak-style': ['error', 'unix'], 43 | 'no-console': [0], 44 | quotes: [ 45 | 'error', 46 | 'single', 47 | {avoidEscape: true, allowTemplateLiterals: true}, 48 | ], 49 | 'require-await': ['error'], 50 | semi: ['error', 'always'], 51 | }, 52 | }; 53 | -------------------------------------------------------------------------------- /.flowconfig: -------------------------------------------------------------------------------- 1 | [ignore] 2 | /node_modules/* 3 | 4 | [include] 5 | 6 | [libs] 7 | node_modules/@solana/web3.js/module.flow.js 8 | flow-typed/ 9 | 10 | [options] 11 | 12 | emoji=true 13 | esproposal.class_instance_fields=enable 14 | esproposal.class_static_fields=enable 15 | esproposal.decorators=ignore 16 | esproposal.export_star_as=enable 17 | module.system.node.resolve_dirname=./src 18 | module.use_strict=true 19 | experimental.const_params=true 20 | include_warnings=true 21 | suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe 22 | suppress_comment=\\(.\\|\n\\)*\\$FlowIssue 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | *.sw[po] 3 | /dist/*bundle.js 4 | /dist/*worker.js 5 | /dist/config.json 6 | /dist/program/ 7 | /.cargo 8 | .env 9 | -------------------------------------------------------------------------------- /.prettierrc.yaml: -------------------------------------------------------------------------------- 1 | arrowParens: "avoid" 2 | bracketSpacing: false 3 | jsxBracketSameLine: false 4 | semi: true 5 | singleQuote: true 6 | tabWidth: 2 7 | trailingComma: "all" 8 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | dist: bionic 2 | sudo: required 3 | language: rust 4 | services: 5 | - docker 6 | cache: 7 | cargo: true 8 | directories: 9 | - "~/.npm" 10 | notifications: 11 | email: false 12 | 13 | install: 14 | - cargo --version 15 | - docker --version 16 | - wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add - 17 | - sudo apt-add-repository "deb http://apt.llvm.org/bionic/ llvm-toolchain-bionic-10 main" 18 | - sudo apt-get update 19 | - sudo apt-get install -y clang-7 --allow-unauthenticated 20 | - sudo apt-get install -y openssl --allow-unauthenticated 21 | - sudo apt-get install -y libssl-dev --allow-unauthenticated 22 | - sudo apt-get install -y libssl1.1 --allow-unauthenticated 23 | - clang-7 --version 24 | - nvm install node 25 | - node --version 26 | - npm install 27 | 28 | script: 29 | - npm run build:program 30 | - cargo test --manifest-path=src/program-test/Cargo.toml 31 | - npm run test 32 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2018 Solana Labs, Inc 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build status][travis-image]][travis-url] 2 | 3 | [travis-image]: https://travis-ci.org/solana-labs/example-token.svg?branch=v1.1 4 | [travis-url]: https://travis-ci.org/solana-labs/example-token 5 | 6 | # Token Example on Solana 7 | 8 | This project demonstrates how to use the [Solana Javascript API](https://github.com/solana-labs/solana-web3.js) 9 | to build, deploy, and interact with an ERC20-like Token example program on the Solana blockchain. 10 | 11 | The project comprises of: 12 | 13 | * A library to interact with the on-chain program 14 | * Test client that exercises the program 15 | 16 | ## Getting Started 17 | 18 | First fetch the npm dependencies, including `@solana/web3.js`, by running: 19 | ```sh 20 | $ npm install 21 | ``` 22 | 23 | ### Select a Network 24 | 25 | This example connects to a local Solana cluster by default. 26 | 27 | To enable on-chain program logs, set the `RUST_LOG` environment variable: 28 | 29 | ```bash 30 | $ export RUST_LOG=solana_runtime::system_instruction_processor=trace,solana_runtime::message_processor=info,solana_bpf_loader=debug,solana_rbpf=debug 31 | ``` 32 | 33 | To start a local Solana cluster run: 34 | ```bash 35 | $ npm run localnet:update 36 | $ npm run localnet:up 37 | ``` 38 | 39 | Solana cluster logs are available with: 40 | ```bash 41 | $ npm run localnet:logs 42 | ``` 43 | 44 | For more details on working with a local cluster, see the [full instructions](https://github.com/solana-labs/solana-web3.js#local-network). 45 | 46 | By default the program will connect to the 47 | beta testnet. To use the edge testnet instead, define `export CHANNEL=edge' in 48 | your environment (see [url.js](https://github.com/solana-labs/solana/tree/master/urj.js) for more) 49 | 50 | ### Run the test client 51 | 52 | ```sh 53 | $ npm run start 54 | ``` 55 | 56 | ## Customizing the Program 57 | 58 | To customize the example, make changes to the files under `/src` 59 | 60 | Now when you run `npm run start`, you should see the results of your changes. 61 | 62 | ## Pointing to a public Solana cluster 63 | 64 | Solana maintains three public clusters: 65 | - `devnet` - Development cluster with airdrops enabled 66 | - `testnet` - Tour De Sol test cluster without airdrops enabled 67 | - `mainnet-beta` - Main cluster 68 | 69 | Use npm scripts to configure which cluster. 70 | 71 | To point to `devnet`: 72 | ```bash 73 | $ npm run cluster:devnet 74 | ``` 75 | 76 | To point back to the local cluster: 77 | ```bash 78 | $ npm run cluster:localnet 79 | ``` 80 | -------------------------------------------------------------------------------- /cluster-devnet.env: -------------------------------------------------------------------------------- 1 | LIVE=1 2 | CLUSTER=devnet 3 | -------------------------------------------------------------------------------- /cluster-mainnet-beta.env: -------------------------------------------------------------------------------- 1 | LIVE=1 2 | CLUSTER=mainnet-beta 3 | -------------------------------------------------------------------------------- /cluster-testnet.env: -------------------------------------------------------------------------------- 1 | LIVE=1 2 | CLUSTER=testnet 3 | -------------------------------------------------------------------------------- /flow-typed/bn.js.js: -------------------------------------------------------------------------------- 1 | declare module 'bn.js' { 2 | // TODO: Fill in types 3 | declare module.exports: any; 4 | } 5 | -------------------------------------------------------------------------------- /flow-typed/bs58.js: -------------------------------------------------------------------------------- 1 | declare module 'bs58' { 2 | declare module.exports: { 3 | encode(input: Buffer): string; 4 | decode(input: string): Buffer; 5 | }; 6 | } 7 | -------------------------------------------------------------------------------- /flow-typed/buffer-layout.js: -------------------------------------------------------------------------------- 1 | declare module 'buffer-layout' { 2 | // TODO: Fill in types 3 | declare module.exports: any; 4 | } 5 | -------------------------------------------------------------------------------- /flow-typed/cbor.js: -------------------------------------------------------------------------------- 1 | declare module 'cbor' { 2 | declare module.exports: { 3 | decode(input: Buffer): Object; 4 | encode(input: any): Buffer; 5 | }; 6 | } 7 | -------------------------------------------------------------------------------- /flow-typed/event-emitter.js: -------------------------------------------------------------------------------- 1 | // flow-typed signature: 4f92d81ee3831cb415b4b216cc0679d9 2 | // flow-typed version: <>/event-emitter_v0.3.5/flow_v0.84.0 3 | 4 | /** 5 | * This is an autogenerated libdef stub for: 6 | * 7 | * 'event-emitter' 8 | * 9 | * Fill this stub out by replacing all the `any` types. 10 | * 11 | * Once filled out, we encourage you to share your work with the 12 | * community by sending a pull request to: 13 | * https://github.com/flowtype/flow-typed 14 | */ 15 | 16 | declare module 'event-emitter' { 17 | declare module.exports: any; 18 | } 19 | 20 | /** 21 | * We include stubs for each file inside this npm package in case you need to 22 | * require those files directly. Feel free to delete any files that aren't 23 | * needed. 24 | */ 25 | declare module 'event-emitter/all-off' { 26 | declare module.exports: any; 27 | } 28 | 29 | declare module 'event-emitter/benchmark/many-on' { 30 | declare module.exports: any; 31 | } 32 | 33 | declare module 'event-emitter/benchmark/single-on' { 34 | declare module.exports: any; 35 | } 36 | 37 | declare module 'event-emitter/emit-error' { 38 | declare module.exports: any; 39 | } 40 | 41 | declare module 'event-emitter/has-listeners' { 42 | declare module.exports: any; 43 | } 44 | 45 | declare module 'event-emitter/pipe' { 46 | declare module.exports: any; 47 | } 48 | 49 | declare module 'event-emitter/test/all-off' { 50 | declare module.exports: any; 51 | } 52 | 53 | declare module 'event-emitter/test/emit-error' { 54 | declare module.exports: any; 55 | } 56 | 57 | declare module 'event-emitter/test/has-listeners' { 58 | declare module.exports: any; 59 | } 60 | 61 | declare module 'event-emitter/test/index' { 62 | declare module.exports: any; 63 | } 64 | 65 | declare module 'event-emitter/test/pipe' { 66 | declare module.exports: any; 67 | } 68 | 69 | declare module 'event-emitter/test/unify' { 70 | declare module.exports: any; 71 | } 72 | 73 | declare module 'event-emitter/unify' { 74 | declare module.exports: any; 75 | } 76 | 77 | // Filename aliases 78 | declare module 'event-emitter/all-off.js' { 79 | declare module.exports: $Exports<'event-emitter/all-off'>; 80 | } 81 | declare module 'event-emitter/benchmark/many-on.js' { 82 | declare module.exports: $Exports<'event-emitter/benchmark/many-on'>; 83 | } 84 | declare module 'event-emitter/benchmark/single-on.js' { 85 | declare module.exports: $Exports<'event-emitter/benchmark/single-on'>; 86 | } 87 | declare module 'event-emitter/emit-error.js' { 88 | declare module.exports: $Exports<'event-emitter/emit-error'>; 89 | } 90 | declare module 'event-emitter/has-listeners.js' { 91 | declare module.exports: $Exports<'event-emitter/has-listeners'>; 92 | } 93 | declare module 'event-emitter/index' { 94 | declare module.exports: $Exports<'event-emitter'>; 95 | } 96 | declare module 'event-emitter/index.js' { 97 | declare module.exports: $Exports<'event-emitter'>; 98 | } 99 | declare module 'event-emitter/pipe.js' { 100 | declare module.exports: $Exports<'event-emitter/pipe'>; 101 | } 102 | declare module 'event-emitter/test/all-off.js' { 103 | declare module.exports: $Exports<'event-emitter/test/all-off'>; 104 | } 105 | declare module 'event-emitter/test/emit-error.js' { 106 | declare module.exports: $Exports<'event-emitter/test/emit-error'>; 107 | } 108 | declare module 'event-emitter/test/has-listeners.js' { 109 | declare module.exports: $Exports<'event-emitter/test/has-listeners'>; 110 | } 111 | declare module 'event-emitter/test/index.js' { 112 | declare module.exports: $Exports<'event-emitter/test/index'>; 113 | } 114 | declare module 'event-emitter/test/pipe.js' { 115 | declare module.exports: $Exports<'event-emitter/test/pipe'>; 116 | } 117 | declare module 'event-emitter/test/unify.js' { 118 | declare module.exports: $Exports<'event-emitter/test/unify'>; 119 | } 120 | declare module 'event-emitter/unify.js' { 121 | declare module.exports: $Exports<'event-emitter/unify'>; 122 | } 123 | -------------------------------------------------------------------------------- /flow-typed/json-to-pretty-yaml.js: -------------------------------------------------------------------------------- 1 | // flow-typed signature: a65f8ee05f35bc382c3b0f8740bc609d 2 | // flow-typed version: <>/json-to-pretty-yaml_v1.2.2/flow_v0.84.0 3 | 4 | /** 5 | * This is an autogenerated libdef stub for: 6 | * 7 | * 'json-to-pretty-yaml' 8 | * 9 | * Fill this stub out by replacing all the `any` types. 10 | * 11 | * Once filled out, we encourage you to share your work with the 12 | * community by sending a pull request to: 13 | * https://github.com/flowtype/flow-typed 14 | */ 15 | 16 | declare module 'json-to-pretty-yaml' { 17 | declare module.exports: any; 18 | } 19 | 20 | /** 21 | * We include stubs for each file inside this npm package in case you need to 22 | * require those files directly. Feel free to delete any files that aren't 23 | * needed. 24 | */ 25 | declare module 'json-to-pretty-yaml/index.test' { 26 | declare module.exports: any; 27 | } 28 | 29 | // Filename aliases 30 | declare module 'json-to-pretty-yaml/index' { 31 | declare module.exports: $Exports<'json-to-pretty-yaml'>; 32 | } 33 | declare module 'json-to-pretty-yaml/index.js' { 34 | declare module.exports: $Exports<'json-to-pretty-yaml'>; 35 | } 36 | declare module 'json-to-pretty-yaml/index.test.js' { 37 | declare module.exports: $Exports<'json-to-pretty-yaml/index.test'>; 38 | } 39 | -------------------------------------------------------------------------------- /flow-typed/mkdirp-promise_vx.x.x.js: -------------------------------------------------------------------------------- 1 | // flow-typed signature: 65e18196703cbb222ea294226e99826d 2 | // flow-typed version: <>/mkdirp-promise_v5.0.1/flow_v0.84.0 3 | 4 | /** 5 | * This is an autogenerated libdef stub for: 6 | * 7 | * 'mkdirp-promise' 8 | * 9 | * Fill this stub out by replacing all the `any` types. 10 | * 11 | * Once filled out, we encourage you to share your work with the 12 | * community by sending a pull request to: 13 | * https://github.com/flowtype/flow-typed 14 | */ 15 | 16 | declare module 'mkdirp-promise' { 17 | declare module.exports: any; 18 | } 19 | 20 | /** 21 | * We include stubs for each file inside this npm package in case you need to 22 | * require those files directly. Feel free to delete any files that aren't 23 | * needed. 24 | */ 25 | declare module 'mkdirp-promise/lib/index' { 26 | declare module.exports: any; 27 | } 28 | 29 | // Filename aliases 30 | declare module 'mkdirp-promise/lib/index.js' { 31 | declare module.exports: $Exports<'mkdirp-promise/lib/index'>; 32 | } 33 | -------------------------------------------------------------------------------- /flow-typed/npm/mz_vx.x.x.js: -------------------------------------------------------------------------------- 1 | // flow-typed signature: ed29f42bf4f4916e4f3ba1f5e7343c9d 2 | // flow-typed version: <>/mz_v2.7.0/flow_v0.81.0 3 | 4 | /** 5 | * This is an autogenerated libdef stub for: 6 | * 7 | * 'mz' 8 | * 9 | * Fill this stub out by replacing all the `any` types. 10 | * 11 | * Once filled out, we encourage you to share your work with the 12 | * community by sending a pull request to: 13 | * https://github.com/flowtype/flow-typed 14 | */ 15 | 16 | declare module 'mz' { 17 | declare module.exports: any; 18 | } 19 | 20 | /** 21 | * We include stubs for each file inside this npm package in case you need to 22 | * require those files directly. Feel free to delete any files that aren't 23 | * needed. 24 | */ 25 | declare module 'mz/child_process' { 26 | declare module.exports: any; 27 | } 28 | 29 | declare module 'mz/crypto' { 30 | declare module.exports: any; 31 | } 32 | 33 | declare module 'mz/dns' { 34 | declare module.exports: any; 35 | } 36 | 37 | declare module 'mz/fs' { 38 | declare module.exports: any; 39 | } 40 | 41 | declare module 'mz/readline' { 42 | declare module.exports: any; 43 | } 44 | 45 | declare module 'mz/zlib' { 46 | declare module.exports: any; 47 | } 48 | 49 | // Filename aliases 50 | declare module 'mz/child_process.js' { 51 | declare module.exports: $Exports<'mz/child_process'>; 52 | } 53 | declare module 'mz/crypto.js' { 54 | declare module.exports: $Exports<'mz/crypto'>; 55 | } 56 | declare module 'mz/dns.js' { 57 | declare module.exports: $Exports<'mz/dns'>; 58 | } 59 | declare module 'mz/fs.js' { 60 | declare module.exports: $Exports<'mz/fs'>; 61 | } 62 | declare module 'mz/index' { 63 | declare module.exports: $Exports<'mz'>; 64 | } 65 | declare module 'mz/index.js' { 66 | declare module.exports: $Exports<'mz'>; 67 | } 68 | declare module 'mz/readline.js' { 69 | declare module.exports: $Exports<'mz/readline'>; 70 | } 71 | declare module 'mz/zlib.js' { 72 | declare module.exports: $Exports<'mz/zlib'>; 73 | } 74 | -------------------------------------------------------------------------------- /flow-typed/readline-promise.js: -------------------------------------------------------------------------------- 1 | declare module 'readline-promise' { 2 | 3 | declare class ReadLine { 4 | questionAsync(prompt: string): Promise; 5 | write(text: string): void; 6 | } 7 | 8 | declare module.exports: { 9 | createInterface({ 10 | input: Object, 11 | output: Object, 12 | terminal: boolean 13 | }): ReadLine; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /flow-typed/semver.js: -------------------------------------------------------------------------------- 1 | declare module 'semver' { 2 | declare module.exports: any; 3 | } 4 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "token", 3 | "version": "0.0.1", 4 | "description": "", 5 | "repository": { 6 | "type": "git", 7 | "url": "https://github.com/solana-labs/example-token" 8 | }, 9 | "testnetDefaultChannel": "v1.1.8", 10 | "scripts": { 11 | "start": "babel-node src/cli/main.js", 12 | "lint": "npm run pretty && eslint .", 13 | "lint:fix": "npm run lint -- --fix", 14 | "flow": "flow", 15 | "flow:watch": "watch 'flow' . --wait=1 --ignoreDirectoryPattern=/doc/", 16 | "lint:watch": "watch 'npm run lint:fix' . --wait=1", 17 | "bpf-sdk:update": "solana-bpf-sdk-install node_modules/@solana/web3.js && npm run clean:program", 18 | "build:program": "./src/program/do.sh build", 19 | "clean:program": "./src/program/do.sh clean && cargo clean --manifest-path ./src/program-test/Cargo.toml", 20 | "test:program": "./src/program/do.sh test", 21 | "bench:program": "npm run build:program && cargo test --manifest-path ./src/program-test/Cargo.toml -- --nocapture", 22 | "cluster:localnet": "rm -f .env", 23 | "cluster:devnet": "cp cluster-devnet.env .env", 24 | "cluster:testnet": "cp cluster-testnet.env .env", 25 | "cluster:mainnet-beta": "cp cluster-mainnet-beta.env .env", 26 | "localnet:update": "solana-localnet update", 27 | "localnet:up": "set -x; solana-localnet down; set -e; solana-localnet up", 28 | "localnet:down": "solana-localnet down", 29 | "localnet:logs": "solana-localnet logs -f", 30 | "pretty": "prettier --write '{,src/**/}*.js'", 31 | "postinstall": "npm run bpf-sdk:update && cargo update --manifest-path=src/program/Cargo.toml && cargo update --manifest-path=src/program-test/Cargo.toml", 32 | "test": "npm run build:program && npm run flow" 33 | }, 34 | "keywords": [], 35 | "author": "", 36 | "license": "MIT", 37 | "devDependencies": { 38 | "prettier": "^2.0.2" 39 | }, 40 | "dependencies": { 41 | "@solana/web3.js": "^0.43.4", 42 | "babel-cli": "^6.26.0", 43 | "babel-core": "^6.26.3", 44 | "babel-eslint": "^10.0.1", 45 | "babel-loader": "^7.1.5", 46 | "babel-plugin-transform-class-properties": "^6.24.1", 47 | "babel-plugin-transform-function-bind": "^6.22.0", 48 | "babel-plugin-transform-runtime": "^6.23.0", 49 | "babel-preset-env": "^1.7.0", 50 | "babel-preset-flow": "^6.23.0", 51 | "babel-preset-react": "^6.24.1", 52 | "babel-preset-stage-2": "^6.24.1", 53 | "babel-runtime": "^6.26.0", 54 | "bn.js": "^5.0.0", 55 | "body-parser": "^1.18.3", 56 | "buffer-layout": "^1.2.0", 57 | "css-loader": "^3.1.0", 58 | "dotenv": "8.2.0", 59 | "eslint": "^6.1.0", 60 | "eslint-loader": "^3.0.0", 61 | "eslint-plugin-import": "^2.13.0", 62 | "eslint-plugin-react": "^7.11.1", 63 | "event-emitter": "^0.3.5", 64 | "express": "^4.16.4", 65 | "flow-bin": "0.121.0", 66 | "flow-typed": "^3.0.0", 67 | "http-server": "^0.12.0", 68 | "jayson": "^3.0.1", 69 | "json-to-pretty-yaml": "^1.2.2", 70 | "mkdirp-promise": "^5.0.1", 71 | "moment": "^2.22.2", 72 | "mz": "^2.7.0", 73 | "node-fetch": "^2.2.0", 74 | "react": "^16.5.2", 75 | "react-bootstrap": "^1.0.0", 76 | "react-dom": "^16.5.2", 77 | "readline-promise": "^1.0.3", 78 | "semver": "^7.0.0", 79 | "superstruct": "^0.8.0", 80 | "watch": "^1.0.2", 81 | "webpack": "^4.20.2", 82 | "webpack-cli": "^3.1.1", 83 | "webpack-dev-server": "^3.1.9" 84 | }, 85 | "engines": { 86 | "node": "11.x" 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/cli/main.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Exercises the command-line based token program 3 | * 4 | * @flow 5 | */ 6 | 7 | import { 8 | loadTokenProgram, 9 | createNewToken, 10 | createNewTokenAccount, 11 | transfer, 12 | approveRevoke, 13 | invalidApprove, 14 | failOnApproveOverspend, 15 | setOwner, 16 | } from './token-test'; 17 | 18 | async function main() { 19 | console.log('Run test: loadTokenProgram'); 20 | await loadTokenProgram(); 21 | console.log('Run test: createNewToken'); 22 | await createNewToken(); 23 | console.log('Run test: createNewTokenAccount'); 24 | await createNewTokenAccount(); 25 | console.log('Run test: transfer'); 26 | await transfer(); 27 | console.log('Run test: approveRevoke'); 28 | await approveRevoke(); 29 | console.log('Run test: invalidApprove'); 30 | await invalidApprove(); 31 | console.log('Run test: failOnApproveOverspend'); 32 | await failOnApproveOverspend(); 33 | console.log('Run test: setOwner'); 34 | await setOwner(); 35 | console.log('Success\n'); 36 | } 37 | 38 | main() 39 | .catch(err => { 40 | console.error(err); 41 | }) 42 | .then(() => process.exit()); 43 | -------------------------------------------------------------------------------- /src/cli/token-test.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import fs from 'mz/fs'; 4 | import {Connection, BpfLoader, PublicKey} from '@solana/web3.js'; 5 | import semver from 'semver'; 6 | 7 | import {Token, TokenAmount} from '../client/token'; 8 | import {url} from '../../url'; 9 | import {newAccountWithLamports} from '../client/util/new-account-with-lamports'; 10 | import {sleep} from '../client/util/sleep'; 11 | 12 | // Loaded token program's program id 13 | let programId: PublicKey; 14 | 15 | // A token created by the next test and used by all subsequent tests 16 | let testToken: Token; 17 | 18 | // Initial owner of the token supply 19 | let initialOwner; 20 | let initialOwnerTokenAccount: PublicKey; 21 | 22 | function assert(condition, message) { 23 | if (!condition) { 24 | console.log(Error().stack + ':token-test.js'); 25 | throw message || 'Assertion failed'; 26 | } 27 | } 28 | 29 | async function didThrow(func, args): Promise { 30 | try { 31 | await func.apply(args); 32 | } catch (e) { 33 | return true; 34 | } 35 | return false; 36 | } 37 | 38 | let connection; 39 | async function getConnection(): Promise { 40 | if (connection) return connection; 41 | 42 | let newConnection = new Connection(url); 43 | const version = await newConnection.getVersion(); 44 | 45 | // commitment params are only supported >= 0.21.0 46 | const solanaCoreVersion = version['solana-core'].split(' ')[0]; 47 | if (semver.gte(solanaCoreVersion, '0.21.0')) { 48 | newConnection = new Connection(url, 'recent'); 49 | } 50 | 51 | // eslint-disable-next-line require-atomic-updates 52 | connection = newConnection; 53 | console.log('Connection to cluster established:', url, version); 54 | return connection; 55 | } 56 | 57 | export async function loadTokenProgram(): Promise { 58 | const NUM_RETRIES = 500; /* allow some number of retries */ 59 | const data = await fs.readFile( 60 | 'src/program/target/bpfel-unknown-unknown/release/solana_bpf_token.so', 61 | ); 62 | const connection = await getConnection(); 63 | const {feeCalculator} = await connection.getRecentBlockhash(); 64 | const balanceNeeded = 65 | feeCalculator.lamportsPerSignature * 66 | (BpfLoader.getMinNumSignatures(data.length) + NUM_RETRIES) + 67 | (await connection.getMinimumBalanceForRentExemption(data.length)); 68 | 69 | const from = await newAccountWithLamports(connection, balanceNeeded); 70 | console.log('Loading Token program...'); 71 | programId = await BpfLoader.load(connection, from, data); 72 | } 73 | 74 | export async function createNewToken(): Promise { 75 | const connection = await getConnection(); 76 | const balanceNeeded = 77 | (await Token.getMinBalanceRentForExemptToken(connection)) + 78 | (await Token.getMinBalanceRentForExemptTokenAccount(connection)); 79 | initialOwner = await newAccountWithLamports(connection, balanceNeeded); 80 | [testToken, initialOwnerTokenAccount] = await Token.createNewToken( 81 | connection, 82 | initialOwner, 83 | new TokenAmount(10000), 84 | 2, 85 | programId, 86 | ); 87 | 88 | const tokenInfo = await testToken.tokenInfo(); 89 | assert(tokenInfo.supply.toNumber() == 10000); 90 | assert(tokenInfo.decimals == 2); 91 | 92 | const accountInfo = await testToken.accountInfo(initialOwnerTokenAccount); 93 | assert(accountInfo.token.equals(testToken.token)); 94 | assert(accountInfo.owner.equals(initialOwner.publicKey)); 95 | assert(accountInfo.amount.toNumber() == 10000); 96 | assert(accountInfo.source == null); 97 | assert(accountInfo.originalAmount.toNumber() == 0); 98 | } 99 | 100 | export async function createNewTokenAccount(): Promise { 101 | const connection = await getConnection(); 102 | const balanceNeeded = await Token.getMinBalanceRentForExemptTokenAccount( 103 | connection, 104 | ); 105 | const destOwner = await newAccountWithLamports(connection, balanceNeeded); 106 | const dest = await testToken.newAccount(destOwner); 107 | const accountInfo = await testToken.accountInfo(dest); 108 | assert(accountInfo.token.equals(testToken.token)); 109 | assert(accountInfo.owner.equals(destOwner.publicKey)); 110 | assert(accountInfo.amount.toNumber() == 0); 111 | assert(accountInfo.source == null); 112 | } 113 | 114 | export async function transfer(): Promise { 115 | const connection = await getConnection(); 116 | const balanceNeeded = await Token.getMinBalanceRentForExemptTokenAccount( 117 | connection, 118 | ); 119 | const destOwner = await newAccountWithLamports(connection, balanceNeeded); 120 | const dest = await testToken.newAccount(destOwner); 121 | 122 | await testToken.transfer(initialOwner, initialOwnerTokenAccount, dest, 123); 123 | await sleep(500); 124 | 125 | const destAccountInfo = await testToken.accountInfo(dest); 126 | assert(destAccountInfo.amount.toNumber() == 123); 127 | } 128 | 129 | export async function approveRevoke(): Promise { 130 | if (programId == null) { 131 | console.log('test skipped, requires "load token program" to succeed'); 132 | return; 133 | } 134 | 135 | const connection = await getConnection(); 136 | const balanceNeeded = await Token.getMinBalanceRentForExemptTokenAccount( 137 | connection, 138 | ); 139 | const delegateOwner = await newAccountWithLamports(connection, balanceNeeded); 140 | const delegate = await testToken.newAccount( 141 | delegateOwner, 142 | initialOwnerTokenAccount, 143 | ); 144 | 145 | await testToken.approve( 146 | initialOwner, 147 | initialOwnerTokenAccount, 148 | delegate, 149 | 456, 150 | ); 151 | 152 | let delegateAccountInfo = await testToken.accountInfo(delegate); 153 | assert(delegateAccountInfo.amount.toNumber() == 456); 154 | assert(delegateAccountInfo.originalAmount.toNumber() == 456); 155 | if (delegateAccountInfo.source === null) { 156 | throw new Error('source should not be null'); 157 | } else { 158 | assert(delegateAccountInfo.source.equals(initialOwnerTokenAccount)); 159 | } 160 | 161 | await testToken.revoke(initialOwner, initialOwnerTokenAccount, delegate); 162 | delegateAccountInfo = await testToken.accountInfo(delegate); 163 | assert(delegateAccountInfo.amount.toNumber() == 0); 164 | assert(delegateAccountInfo.originalAmount.toNumber() == 0); 165 | if (delegateAccountInfo.source === null) { 166 | throw new Error('source should not be null'); 167 | } else { 168 | assert(delegateAccountInfo.source.equals(initialOwnerTokenAccount)); 169 | } 170 | } 171 | 172 | export async function invalidApprove(): Promise { 173 | const connection = await getConnection(); 174 | const balanceNeeded = 175 | (await Token.getMinBalanceRentForExemptTokenAccount(connection)) * 3; 176 | const owner = await newAccountWithLamports(connection, balanceNeeded); 177 | const account1 = await testToken.newAccount(owner); 178 | const account1Delegate = await testToken.newAccount(owner, account1); 179 | const account2 = await testToken.newAccount(owner); 180 | 181 | // account2 is not a delegate account of account1 182 | assert(didThrow(testToken.approve, [owner, account1, account2, 123])); 183 | // account1Delegate is not a delegate account of account2 184 | assert(didThrow(testToken.approve, [owner, account2, account1Delegate, 123])); 185 | } 186 | 187 | export async function failOnApproveOverspend(): Promise { 188 | const connection = await getConnection(); 189 | const balanceNeeded = 190 | (await Token.getMinBalanceRentForExemptTokenAccount(connection)) * 3; 191 | const owner = await newAccountWithLamports(connection, balanceNeeded); 192 | const account1 = await testToken.newAccount(owner); 193 | const account1Delegate = await testToken.newAccount(owner, account1); 194 | const account2 = await testToken.newAccount(owner); 195 | 196 | await testToken.transfer( 197 | initialOwner, 198 | initialOwnerTokenAccount, 199 | account1, 200 | 10, 201 | ); 202 | 203 | await testToken.approve(owner, account1, account1Delegate, 2); 204 | 205 | let delegateAccountInfo = await testToken.accountInfo(account1Delegate); 206 | assert(delegateAccountInfo.amount.toNumber() == 2); 207 | assert(delegateAccountInfo.originalAmount.toNumber() == 2); 208 | 209 | await testToken.transfer(owner, account1Delegate, account2, 1); 210 | 211 | delegateAccountInfo = await testToken.accountInfo(account1Delegate); 212 | assert(delegateAccountInfo.amount.toNumber() == 1); 213 | assert(delegateAccountInfo.originalAmount.toNumber() == 2); 214 | 215 | await testToken.transfer(owner, account1Delegate, account2, 1); 216 | 217 | delegateAccountInfo = await testToken.accountInfo(account1Delegate); 218 | assert(delegateAccountInfo.amount.toNumber() == 0); 219 | assert(delegateAccountInfo.originalAmount.toNumber() == 2); 220 | 221 | assert(didThrow(testToken.transfer, [owner, account1Delegate, account2, 1])); 222 | } 223 | 224 | export async function setOwner(): Promise { 225 | const connection = await getConnection(); 226 | const balanceNeeded = await Token.getMinBalanceRentForExemptTokenAccount( 227 | connection, 228 | ); 229 | const owner = await newAccountWithLamports(connection, balanceNeeded); 230 | const newOwner = await newAccountWithLamports(connection, balanceNeeded); 231 | const account = await testToken.newAccount(owner); 232 | 233 | await testToken.setOwner(owner, account, newOwner.publicKey); 234 | assert(didThrow(testToken.setOwner, [owner, account, newOwner.publicKey])); 235 | await testToken.setOwner(newOwner, account, owner.publicKey); 236 | } 237 | -------------------------------------------------------------------------------- /src/client/layout.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import * as BufferLayout from 'buffer-layout'; 4 | 5 | /** 6 | * Layout for a public key 7 | */ 8 | export const publicKey = (property: string = 'publicKey'): Object => { 9 | return BufferLayout.blob(32, property); 10 | }; 11 | 12 | /** 13 | * Layout for a 64bit unsigned value 14 | */ 15 | export const uint64 = (property: string = 'uint64'): Object => { 16 | return BufferLayout.blob(8, property); 17 | }; 18 | 19 | /** 20 | * Layout for a Rust String type 21 | */ 22 | export const rustString = (property: string = 'string') => { 23 | const rsl = BufferLayout.struct( 24 | [ 25 | BufferLayout.u32('length'), 26 | BufferLayout.u32('lengthPadding'), 27 | BufferLayout.blob(BufferLayout.offset(BufferLayout.u32(), -8), 'chars'), 28 | ], 29 | property, 30 | ); 31 | const _decode = rsl.decode.bind(rsl); 32 | const _encode = rsl.encode.bind(rsl); 33 | 34 | rsl.decode = (buffer, offset) => { 35 | const data = _decode(buffer, offset); 36 | return data.chars.toString('utf8'); 37 | }; 38 | 39 | rsl.encode = (str, buffer, offset) => { 40 | const data = { 41 | chars: Buffer.from(str, 'utf8'), 42 | }; 43 | return _encode(data, buffer, offset); 44 | }; 45 | 46 | return rsl; 47 | }; 48 | -------------------------------------------------------------------------------- /src/client/token.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @flow 3 | */ 4 | 5 | import assert from 'assert'; 6 | import BN from 'bn.js'; 7 | import * as BufferLayout from 'buffer-layout'; 8 | import { 9 | Account, 10 | PublicKey, 11 | SystemProgram, 12 | Transaction, 13 | TransactionInstruction, 14 | } from '@solana/web3.js'; 15 | import type {Connection, TransactionSignature} from '@solana/web3.js'; 16 | 17 | import * as Layout from './layout'; 18 | import {sendAndConfirmTransaction} from './util/send-and-confirm-transaction'; 19 | 20 | /** 21 | * Some amount of tokens 22 | */ 23 | export class TokenAmount extends BN { 24 | /** 25 | * Convert to Buffer representation 26 | */ 27 | toBuffer(): Buffer { 28 | const a = super.toArray().reverse(); 29 | const b = Buffer.from(a); 30 | if (b.length === 8) { 31 | return b; 32 | } 33 | assert(b.length < 8, 'TokenAmount too large'); 34 | 35 | const zeroPad = Buffer.alloc(8); 36 | b.copy(zeroPad); 37 | return zeroPad; 38 | } 39 | 40 | /** 41 | * Construct a TokenAmount from Buffer representation 42 | */ 43 | static fromBuffer(buffer: Buffer): TokenAmount { 44 | assert(buffer.length === 8, `Invalid buffer length: ${buffer.length}`); 45 | return new BN( 46 | [...buffer] 47 | .reverse() 48 | .map(i => `00${i.toString(16)}`.slice(-2)) 49 | .join(''), 50 | 16, 51 | ); 52 | } 53 | } 54 | 55 | /** 56 | * Information about a token 57 | */ 58 | type TokenInfo = {| 59 | /** 60 | * Total supply of tokens 61 | */ 62 | supply: TokenAmount, 63 | 64 | /** 65 | * Number of base 10 digits to the right of the decimal place 66 | */ 67 | decimals: number, 68 | |}; 69 | 70 | /** 71 | * @private 72 | */ 73 | const TokenInfoLayout = BufferLayout.struct([ 74 | BufferLayout.u8('state'), 75 | Layout.uint64('supply'), 76 | BufferLayout.u8('decimals'), 77 | ]); 78 | 79 | /** 80 | * Information about a token account 81 | */ 82 | type TokenAccountInfo = {| 83 | /** 84 | * The kind of token this account holds 85 | */ 86 | token: PublicKey, 87 | 88 | /** 89 | * Owner of this account 90 | */ 91 | owner: PublicKey, 92 | 93 | /** 94 | * Amount of tokens this account holds 95 | */ 96 | amount: TokenAmount, 97 | 98 | /** 99 | * The source account for the tokens. 100 | * 101 | * If `source` is null, the source is this account. 102 | * If `source` is not null, the `amount` of tokens in this account represent 103 | * an allowance of tokens that may be transferred from the source account 104 | */ 105 | source: null | PublicKey, 106 | 107 | /** 108 | * Original amount of tokens this delegate account was authorized to spend 109 | * If `source` is null, originalAmount is zero 110 | */ 111 | originalAmount: TokenAmount, 112 | |}; 113 | 114 | /** 115 | * @private 116 | */ 117 | const TokenAccountInfoLayout = BufferLayout.struct([ 118 | BufferLayout.u8('state'), 119 | Layout.publicKey('token'), 120 | Layout.publicKey('owner'), 121 | Layout.uint64('amount'), 122 | BufferLayout.nu64('sourceOption'), 123 | Layout.publicKey('source'), 124 | Layout.uint64('originalAmount'), 125 | ]); 126 | 127 | type TokenAndPublicKey = [Token, PublicKey]; // This type exists to workaround an esdoc parse error 128 | 129 | /** 130 | * An ERC20-like Token 131 | */ 132 | export class Token { 133 | /** 134 | * @private 135 | */ 136 | connection: Connection; 137 | 138 | /** 139 | * The public key identifying this token 140 | */ 141 | token: PublicKey; 142 | 143 | /** 144 | * Program Identifier for the Token program 145 | */ 146 | programId: PublicKey; 147 | 148 | /** 149 | * Create a Token object attached to the specific token 150 | * 151 | * @param connection The connection to use 152 | * @param token Public key of the token 153 | * @param programId Optional token programId, uses the system programId by default 154 | */ 155 | constructor(connection: Connection, token: PublicKey, programId: PublicKey) { 156 | Object.assign(this, {connection, token, programId}); 157 | } 158 | 159 | /** 160 | * Get the minimum balance for the token to be rent exempt 161 | * 162 | * @return Number of lamports required 163 | */ 164 | static async getMinBalanceRentForExemptToken( 165 | connection: Connection, 166 | ): Promise { 167 | return await connection.getMinimumBalanceForRentExemption( 168 | TokenInfoLayout.span, 169 | ); 170 | } 171 | 172 | /** 173 | * Get the minimum balance for the token account to be rent exempt 174 | * 175 | * @return Number of lamports required 176 | */ 177 | static async getMinBalanceRentForExemptTokenAccount( 178 | connection: Connection, 179 | ): Promise { 180 | return await connection.getMinimumBalanceForRentExemption( 181 | TokenAccountInfoLayout.span, 182 | ); 183 | } 184 | 185 | /** 186 | * Create a new Token 187 | * 188 | * @param connection The connection to use 189 | * @param owner User account that will own the returned Token Account 190 | * @param supply Total supply of the new token 191 | * @param decimals Location of the decimal place 192 | * @param programId Optional token programId, uses the system programId by default 193 | * @return Token object for the newly minted token, Public key of the Token Account holding the total supply of new tokens 194 | */ 195 | static async createNewToken( 196 | connection: Connection, 197 | owner: Account, 198 | supply: TokenAmount, 199 | decimals: number, 200 | programId: PublicKey, 201 | ): Promise { 202 | const tokenAccount = new Account(); 203 | const token = new Token(connection, tokenAccount.publicKey, programId); 204 | const initialAccountPublicKey = await token.newAccount(owner, null); 205 | 206 | let transaction; 207 | 208 | const dataLayout = BufferLayout.struct([ 209 | BufferLayout.u8('instruction'), 210 | Layout.uint64('supply'), 211 | BufferLayout.nu64('decimals'), 212 | ]); 213 | 214 | let data = Buffer.alloc(1024); 215 | { 216 | const encodeLength = dataLayout.encode( 217 | { 218 | instruction: 0, // NewToken instruction 219 | supply: supply.toBuffer(), 220 | decimals, 221 | }, 222 | data, 223 | ); 224 | data = data.slice(0, encodeLength); 225 | } 226 | 227 | const balanceNeeded = await Token.getMinBalanceRentForExemptToken( 228 | connection, 229 | ); 230 | 231 | // Allocate memory for the tokenAccount account 232 | transaction = SystemProgram.createAccount({ 233 | fromPubkey: owner.publicKey, 234 | newAccountPubkey: tokenAccount.publicKey, 235 | lamports: balanceNeeded, 236 | space: 1 + data.length, 237 | programId, 238 | }); 239 | await sendAndConfirmTransaction( 240 | 'createAccount', 241 | connection, 242 | transaction, 243 | owner, 244 | tokenAccount, 245 | ); 246 | 247 | transaction = new Transaction().add({ 248 | keys: [ 249 | {pubkey: tokenAccount.publicKey, isSigner: true, isWritable: false}, 250 | {pubkey: initialAccountPublicKey, isSigner: false, isWritable: true}, 251 | ], 252 | programId, 253 | data, 254 | }); 255 | await sendAndConfirmTransaction( 256 | 'New tokenAccount', 257 | connection, 258 | transaction, 259 | owner, 260 | tokenAccount, 261 | ); 262 | 263 | return [token, initialAccountPublicKey]; 264 | } 265 | 266 | /** 267 | * Create a new and empty token account. 268 | * 269 | * This account may then be used as a `transfer()` or `approve()` destination 270 | * 271 | * @param owner User account that will own the new token account 272 | * @param source If not null, create a delegate account that when authorized 273 | * may transfer tokens from this `source` account 274 | * @return Public key of the new empty token account 275 | */ 276 | async newAccount( 277 | owner: Account, 278 | source: null | PublicKey = null, 279 | ): Promise { 280 | const tokenAccount = new Account(); 281 | let transaction; 282 | 283 | const dataLayout = BufferLayout.struct([BufferLayout.u8('instruction')]); 284 | 285 | const data = Buffer.alloc(dataLayout.span); 286 | dataLayout.encode( 287 | { 288 | instruction: 1, // NewTokenAccount instruction 289 | }, 290 | data, 291 | ); 292 | 293 | const balanceNeeded = await Token.getMinBalanceRentForExemptTokenAccount( 294 | this.connection, 295 | ); 296 | 297 | // Allocate memory for the token 298 | transaction = SystemProgram.createAccount({ 299 | fromPubkey: owner.publicKey, 300 | newAccountPubkey: tokenAccount.publicKey, 301 | lamports: balanceNeeded, 302 | space: TokenAccountInfoLayout.span, 303 | programId: this.programId, 304 | }); 305 | await sendAndConfirmTransaction( 306 | 'createAccount', 307 | this.connection, 308 | transaction, 309 | owner, 310 | tokenAccount, 311 | ); 312 | 313 | // Initialize the token account 314 | const keys = [ 315 | {pubkey: tokenAccount.publicKey, isSigner: true, isWritable: true}, 316 | {pubkey: owner.publicKey, isSigner: false, isWritable: false}, 317 | {pubkey: this.token, isSigner: false, isWritable: false}, 318 | ]; 319 | if (source) { 320 | keys.push({pubkey: source, isSigner: false, isWritable: false}); 321 | } 322 | transaction = new Transaction().add({ 323 | keys, 324 | programId: this.programId, 325 | data, 326 | }); 327 | await sendAndConfirmTransaction( 328 | 'init tokenAccount', 329 | this.connection, 330 | transaction, 331 | owner, 332 | tokenAccount, 333 | ); 334 | 335 | return tokenAccount.publicKey; 336 | } 337 | 338 | /** 339 | * Retrieve token information 340 | */ 341 | async tokenInfo(): Promise { 342 | const accountInfo = await this.connection.getAccountInfo(this.token); 343 | if (!accountInfo.owner.equals(this.programId)) { 344 | throw new Error( 345 | `Invalid token owner: ${JSON.stringify(accountInfo.owner)}`, 346 | ); 347 | } 348 | 349 | const data = Buffer.from(accountInfo.data); 350 | 351 | const tokenInfo = TokenInfoLayout.decode(data); 352 | if (tokenInfo.state !== 1) { 353 | throw new Error(`Invalid token account data`); 354 | } 355 | tokenInfo.supply = TokenAmount.fromBuffer(tokenInfo.supply); 356 | return tokenInfo; 357 | } 358 | 359 | /** 360 | * Retrieve account information 361 | * 362 | * @param account Public key of the token account 363 | */ 364 | async accountInfo(account: PublicKey): Promise { 365 | const accountInfo = await this.connection.getAccountInfo(account); 366 | if (!accountInfo.owner.equals(this.programId)) { 367 | throw new Error(`Invalid token account owner`); 368 | } 369 | 370 | const data = Buffer.from(accountInfo.data); 371 | const tokenAccountInfo = TokenAccountInfoLayout.decode(data); 372 | 373 | if (tokenAccountInfo.state !== 2) { 374 | throw new Error(`Invalid token account data`); 375 | } 376 | tokenAccountInfo.token = new PublicKey(tokenAccountInfo.token); 377 | tokenAccountInfo.owner = new PublicKey(tokenAccountInfo.owner); 378 | tokenAccountInfo.amount = TokenAmount.fromBuffer(tokenAccountInfo.amount); 379 | if (tokenAccountInfo.sourceOption === 0) { 380 | tokenAccountInfo.source = null; 381 | tokenAccountInfo.originalAmount = new TokenAmount(); 382 | } else { 383 | tokenAccountInfo.source = new PublicKey(tokenAccountInfo.source); 384 | tokenAccountInfo.originalAmount = TokenAmount.fromBuffer( 385 | tokenAccountInfo.originalAmount, 386 | ); 387 | } 388 | 389 | if (!tokenAccountInfo.token.equals(this.token)) { 390 | throw new Error( 391 | `Invalid token account token: ${JSON.stringify( 392 | tokenAccountInfo.token, 393 | )} !== ${JSON.stringify(this.token)}`, 394 | ); 395 | } 396 | return tokenAccountInfo; 397 | } 398 | 399 | /** 400 | * Transfer tokens to another account 401 | * 402 | * @param owner Owner of the source token account 403 | * @param source Source token account 404 | * @param destination Destination token account 405 | * @param amount Number of tokens to transfer 406 | */ 407 | async transfer( 408 | owner: Account, 409 | source: PublicKey, 410 | destination: PublicKey, 411 | amount: number | TokenAmount, 412 | ): Promise { 413 | return await sendAndConfirmTransaction( 414 | 'transfer', 415 | this.connection, 416 | new Transaction().add( 417 | await this.transferInstruction( 418 | owner.publicKey, 419 | source, 420 | destination, 421 | amount, 422 | ), 423 | ), 424 | owner, 425 | ); 426 | } 427 | 428 | /** 429 | * Grant a third-party permission to transfer up the specified number of tokens from an account 430 | * 431 | * @param owner Owner of the source token account 432 | * @param account Public key of the token account 433 | * @param delegate Token account authorized to perform a transfer tokens from the source account 434 | * @param amount Maximum number of tokens the delegate may transfer 435 | */ 436 | async approve( 437 | owner: Account, 438 | account: PublicKey, 439 | delegate: PublicKey, 440 | amount: number | TokenAmount, 441 | ): Promise { 442 | await sendAndConfirmTransaction( 443 | 'approve', 444 | this.connection, 445 | new Transaction().add( 446 | this.approveInstruction(owner.publicKey, account, delegate, amount), 447 | ), 448 | owner, 449 | ); 450 | } 451 | 452 | /** 453 | * Remove approval for the transfer of any remaining tokens 454 | * 455 | * @param owner Owner of the source token account 456 | * @param account Public key of the token account 457 | * @param delegate Token account to revoke authorization from 458 | */ 459 | revoke( 460 | owner: Account, 461 | account: PublicKey, 462 | delegate: PublicKey, 463 | ): Promise { 464 | return this.approve(owner, account, delegate, 0); 465 | } 466 | 467 | /** 468 | * Assign a new owner to the account 469 | * 470 | * @param owner Owner of the token account 471 | * @param account Public key of the token account 472 | * @param newOwner New owner of the token account 473 | */ 474 | async setOwner( 475 | owner: Account, 476 | account: PublicKey, 477 | newOwner: PublicKey, 478 | ): Promise { 479 | await sendAndConfirmTransaction( 480 | 'setOwneer', 481 | this.connection, 482 | new Transaction().add( 483 | this.setOwnerInstruction(owner.publicKey, account, newOwner), 484 | ), 485 | owner, 486 | ); 487 | } 488 | 489 | /** 490 | * Construct a Transfer instruction 491 | * 492 | * @param owner Owner of the source token account 493 | * @param source Source token account 494 | * @param destination Destination token account 495 | * @param amount Number of tokens to transfer 496 | */ 497 | async transferInstruction( 498 | owner: PublicKey, 499 | source: PublicKey, 500 | destination: PublicKey, 501 | amount: number | TokenAmount, 502 | ): Promise { 503 | const accountInfo = await this.accountInfo(source); 504 | if (!owner.equals(accountInfo.owner)) { 505 | throw new Error('Account owner mismatch'); 506 | } 507 | 508 | const dataLayout = BufferLayout.struct([ 509 | BufferLayout.u8('instruction'), 510 | Layout.uint64('amount'), 511 | ]); 512 | 513 | const data = Buffer.alloc(dataLayout.span); 514 | dataLayout.encode( 515 | { 516 | instruction: 2, // Transfer instruction 517 | amount: new TokenAmount(amount).toBuffer(), 518 | }, 519 | data, 520 | ); 521 | 522 | const keys = [ 523 | {pubkey: owner, isSigner: true, isWritable: false}, 524 | {pubkey: source, isSigner: false, isWritable: true}, 525 | {pubkey: destination, isSigner: false, isWritable: true}, 526 | ]; 527 | if (accountInfo.source) { 528 | keys.push({ 529 | pubkey: accountInfo.source, 530 | isSigner: false, 531 | isWritable: true, 532 | }); 533 | } 534 | return new TransactionInstruction({ 535 | keys, 536 | programId: this.programId, 537 | data, 538 | }); 539 | } 540 | 541 | /** 542 | * Construct an Approve instruction 543 | * 544 | * @param owner Owner of the source token account 545 | * @param account Public key of the token account 546 | * @param delegate Token account authorized to perform a transfer tokens from the source account 547 | * @param amount Maximum number of tokens the delegate may transfer 548 | */ 549 | approveInstruction( 550 | owner: PublicKey, 551 | account: PublicKey, 552 | delegate: PublicKey, 553 | amount: number | TokenAmount, 554 | ): TransactionInstruction { 555 | const dataLayout = BufferLayout.struct([ 556 | BufferLayout.u8('instruction'), 557 | Layout.uint64('amount'), 558 | ]); 559 | 560 | const data = Buffer.alloc(dataLayout.span); 561 | dataLayout.encode( 562 | { 563 | instruction: 3, // Approve instruction 564 | amount: new TokenAmount(amount).toBuffer(), 565 | }, 566 | data, 567 | ); 568 | 569 | return new TransactionInstruction({ 570 | keys: [ 571 | {pubkey: owner, isSigner: true, isWritable: false}, 572 | {pubkey: account, isSigner: false, isWritable: true}, 573 | {pubkey: delegate, isSigner: false, isWritable: true}, 574 | ], 575 | programId: this.programId, 576 | data, 577 | }); 578 | } 579 | 580 | /** 581 | * Construct an Revoke instruction 582 | * 583 | * @param owner Owner of the source token account 584 | * @param account Public key of the token account 585 | * @param delegate Token account authorized to perform a transfer tokens from the source account 586 | */ 587 | revokeInstruction( 588 | owner: PublicKey, 589 | account: PublicKey, 590 | delegate: PublicKey, 591 | ): TransactionInstruction { 592 | return this.approveInstruction(owner, account, delegate, 0); 593 | } 594 | 595 | /** 596 | * Construct a SetOwner instruction 597 | * 598 | * @param owner Owner of the token account 599 | * @param account Public key of the token account 600 | * @param newOwner New owner of the token account 601 | */ 602 | setOwnerInstruction( 603 | owner: PublicKey, 604 | account: PublicKey, 605 | newOwner: PublicKey, 606 | ): TransactionInstruction { 607 | const dataLayout = BufferLayout.struct([BufferLayout.u8('instruction')]); 608 | 609 | const data = Buffer.alloc(dataLayout.span); 610 | dataLayout.encode( 611 | { 612 | instruction: 4, // SetOwner instruction 613 | }, 614 | data, 615 | ); 616 | 617 | return new TransactionInstruction({ 618 | keys: [ 619 | {pubkey: owner, isSigner: true, isWritable: false}, 620 | {pubkey: account, isSigner: false, isWritable: true}, 621 | {pubkey: newOwner, isSigner: false, isWritable: true}, 622 | ], 623 | programId: this.programId, 624 | data, 625 | }); 626 | } 627 | } 628 | -------------------------------------------------------------------------------- /src/client/util/new-account-with-lamports.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import {Account, Connection} from '@solana/web3.js'; 4 | 5 | import {sleep} from './sleep'; 6 | 7 | export async function newAccountWithLamports( 8 | connection: Connection, 9 | lamports: number = 1000000, 10 | ): Promise { 11 | const account = new Account(); 12 | 13 | let retries = 10; 14 | await connection.requestAirdrop(account.publicKey, lamports); 15 | for (;;) { 16 | await sleep(500); 17 | if (lamports == (await connection.getBalance(account.publicKey))) { 18 | return account; 19 | } 20 | if (--retries <= 0) { 21 | break; 22 | } 23 | console.log('Airdrop retry ' + retries); 24 | } 25 | throw new Error(`Airdrop of ${lamports} failed`); 26 | } 27 | -------------------------------------------------------------------------------- /src/client/util/new-system-account-with-airdrop.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import {Account, Connection} from '@solana/web3.js'; 4 | 5 | /** 6 | * Create a new system account and airdrop it some lamports 7 | * 8 | * @private 9 | */ 10 | export async function newSystemAccountWithAirdrop( 11 | connection: Connection, 12 | lamports: number = 1, 13 | ): Promise { 14 | const account = new Account(); 15 | await connection.requestAirdrop(account.publicKey, lamports); 16 | return account; 17 | } 18 | -------------------------------------------------------------------------------- /src/client/util/send-and-confirm-transaction.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import {sendAndConfirmTransaction as realSendAndConfirmTransaction} from '@solana/web3.js'; 4 | import type {Account, Connection, Transaction} from '@solana/web3.js'; 5 | import YAML from 'json-to-pretty-yaml'; 6 | 7 | import {newSystemAccountWithAirdrop} from './new-system-account-with-airdrop'; 8 | 9 | type TransactionNotification = (string, string) => void; 10 | 11 | let notify: TransactionNotification = () => undefined; 12 | 13 | export function onTransaction(callback: TransactionNotification) { 14 | notify = callback; 15 | } 16 | 17 | let payerAccount: Account | null = null; 18 | export async function sendAndConfirmTransaction( 19 | title: string, 20 | connection: Connection, 21 | transaction: Transaction, 22 | ...signers: Array 23 | ): Promise { 24 | const when = Date.now(); 25 | 26 | if (!payerAccount) { 27 | const {feeCalculator} = await connection.getRecentBlockhash(); 28 | const fees = feeCalculator.lamportsPerSignature * 100; // wag 29 | const newPayerAccount = await newSystemAccountWithAirdrop(connection, fees); 30 | // eslint-disable-next-line require-atomic-updates 31 | payerAccount = payerAccount || newPayerAccount; 32 | } 33 | 34 | const signature = await realSendAndConfirmTransaction( 35 | connection, 36 | transaction, 37 | payerAccount, 38 | ...signers, 39 | ); 40 | 41 | const body = { 42 | time: new Date(when).toString(), 43 | from: signers[0].publicKey.toBase58(), 44 | signature, 45 | instructions: transaction.instructions.map(i => { 46 | return { 47 | keys: i.keys.map(keyObj => keyObj.pubkey.toBase58()), 48 | programId: i.programId.toBase58(), 49 | data: '0x' + i.data.toString('hex'), 50 | }; 51 | }), 52 | }; 53 | 54 | notify(title, YAML.stringify(body).replace(/"/g, '')); 55 | } 56 | -------------------------------------------------------------------------------- /src/client/util/sleep.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | // zzz 4 | export function sleep(ms: number): Promise { 5 | return new Promise(resolve => setTimeout(resolve, ms)); 6 | } 7 | -------------------------------------------------------------------------------- /src/program-test/.gitignore: -------------------------------------------------------------------------------- 1 | /target/ 2 | -------------------------------------------------------------------------------- /src/program-test/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | [[package]] 4 | name = "aho-corasick" 5 | version = "0.7.10" 6 | source = "registry+https://github.com/rust-lang/crates.io-index" 7 | checksum = "8716408b8bc624ed7f65d223ddb9ac2d044c0547b6fa4b0d554f3a9540496ada" 8 | dependencies = [ 9 | "memchr", 10 | ] 11 | 12 | [[package]] 13 | name = "ansi_term" 14 | version = "0.11.0" 15 | source = "registry+https://github.com/rust-lang/crates.io-index" 16 | checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" 17 | dependencies = [ 18 | "winapi 0.3.8", 19 | ] 20 | 21 | [[package]] 22 | name = "ar" 23 | version = "0.6.2" 24 | source = "registry+https://github.com/rust-lang/crates.io-index" 25 | checksum = "579681b3fecd1e9d6b5ce6969e05f9feb913f296eddaf595be1166a5ca597bc4" 26 | dependencies = [ 27 | "byteorder", 28 | ] 29 | 30 | [[package]] 31 | name = "arrayref" 32 | version = "0.3.6" 33 | source = "registry+https://github.com/rust-lang/crates.io-index" 34 | checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544" 35 | 36 | [[package]] 37 | name = "ascii" 38 | version = "0.7.1" 39 | source = "registry+https://github.com/rust-lang/crates.io-index" 40 | checksum = "3ae7d751998c189c1d4468cf0a39bb2eae052a9c58d50ebb3b9591ee3813ad50" 41 | 42 | [[package]] 43 | name = "assert_matches" 44 | version = "1.3.0" 45 | source = "registry+https://github.com/rust-lang/crates.io-index" 46 | checksum = "7deb0a829ca7bcfaf5da70b073a8d128619259a7be8216a355e23f00763059e5" 47 | 48 | [[package]] 49 | name = "atty" 50 | version = "0.2.14" 51 | source = "registry+https://github.com/rust-lang/crates.io-index" 52 | checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" 53 | dependencies = [ 54 | "hermit-abi", 55 | "libc", 56 | "winapi 0.3.8", 57 | ] 58 | 59 | [[package]] 60 | name = "autocfg" 61 | version = "0.1.7" 62 | source = "registry+https://github.com/rust-lang/crates.io-index" 63 | checksum = "1d49d90015b3c36167a20fe2810c5cd875ad504b39cff3d4eae7977e6b7c1cb2" 64 | 65 | [[package]] 66 | name = "autocfg" 67 | version = "1.0.0" 68 | source = "registry+https://github.com/rust-lang/crates.io-index" 69 | checksum = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d" 70 | 71 | [[package]] 72 | name = "backtrace" 73 | version = "0.3.46" 74 | source = "registry+https://github.com/rust-lang/crates.io-index" 75 | checksum = "b1e692897359247cc6bb902933361652380af0f1b7651ae5c5013407f30e109e" 76 | dependencies = [ 77 | "backtrace-sys", 78 | "cfg-if", 79 | "libc", 80 | "rustc-demangle", 81 | "serde", 82 | ] 83 | 84 | [[package]] 85 | name = "backtrace-sys" 86 | version = "0.1.37" 87 | source = "registry+https://github.com/rust-lang/crates.io-index" 88 | checksum = "18fbebbe1c9d1f383a9cc7e8ccdb471b91c8d024ee9c2ca5b5346121fe8b4399" 89 | dependencies = [ 90 | "cc", 91 | "libc", 92 | ] 93 | 94 | [[package]] 95 | name = "base64" 96 | version = "0.11.0" 97 | source = "registry+https://github.com/rust-lang/crates.io-index" 98 | checksum = "b41b7ea54a0c9d92199de89e20e58d49f02f8e699814ef3fdf266f6f748d15c7" 99 | 100 | [[package]] 101 | name = "bincode" 102 | version = "1.2.1" 103 | source = "registry+https://github.com/rust-lang/crates.io-index" 104 | checksum = "5753e2a71534719bf3f4e57006c3a4f0d2c672a4b676eec84161f763eca87dbf" 105 | dependencies = [ 106 | "byteorder", 107 | "serde", 108 | ] 109 | 110 | [[package]] 111 | name = "bit-vec" 112 | version = "0.4.4" 113 | source = "registry+https://github.com/rust-lang/crates.io-index" 114 | checksum = "02b4ff8b16e6076c3e14220b39fbc1fabb6737522281a388998046859400895f" 115 | 116 | [[package]] 117 | name = "bit-vec" 118 | version = "0.5.1" 119 | source = "registry+https://github.com/rust-lang/crates.io-index" 120 | checksum = "f59bbe95d4e52a6398ec21238d31577f2b28a9d86807f06ca59d191d8440d0bb" 121 | 122 | [[package]] 123 | name = "bitflags" 124 | version = "1.2.1" 125 | source = "registry+https://github.com/rust-lang/crates.io-index" 126 | checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" 127 | 128 | [[package]] 129 | name = "block-buffer" 130 | version = "0.3.3" 131 | source = "registry+https://github.com/rust-lang/crates.io-index" 132 | checksum = "a076c298b9ecdb530ed9d967e74a6027d6a7478924520acddcddc24c1c8ab3ab" 133 | dependencies = [ 134 | "arrayref", 135 | "byte-tools 0.2.0", 136 | ] 137 | 138 | [[package]] 139 | name = "block-buffer" 140 | version = "0.7.3" 141 | source = "registry+https://github.com/rust-lang/crates.io-index" 142 | checksum = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b" 143 | dependencies = [ 144 | "block-padding", 145 | "byte-tools 0.3.1", 146 | "byteorder", 147 | "generic-array 0.12.3", 148 | ] 149 | 150 | [[package]] 151 | name = "block-padding" 152 | version = "0.1.5" 153 | source = "registry+https://github.com/rust-lang/crates.io-index" 154 | checksum = "fa79dedbb091f449f1f39e53edf88d5dbe95f895dae6135a8d7b881fb5af73f5" 155 | dependencies = [ 156 | "byte-tools 0.3.1", 157 | ] 158 | 159 | [[package]] 160 | name = "bloom" 161 | version = "0.3.2" 162 | source = "registry+https://github.com/rust-lang/crates.io-index" 163 | checksum = "d00ac8e5056d6d65376a3c1aa5c7c34850d6949ace17f0266953a254eb3d6fe8" 164 | dependencies = [ 165 | "bit-vec 0.4.4", 166 | ] 167 | 168 | [[package]] 169 | name = "bs58" 170 | version = "0.3.1" 171 | source = "registry+https://github.com/rust-lang/crates.io-index" 172 | checksum = "476e9cd489f9e121e02ffa6014a8ef220ecb15c05ed23fc34cca13925dc283fb" 173 | 174 | [[package]] 175 | name = "bumpalo" 176 | version = "3.2.1" 177 | source = "registry+https://github.com/rust-lang/crates.io-index" 178 | checksum = "12ae9db68ad7fac5fe51304d20f016c911539251075a214f8e663babefa35187" 179 | 180 | [[package]] 181 | name = "bv" 182 | version = "0.11.1" 183 | source = "registry+https://github.com/rust-lang/crates.io-index" 184 | checksum = "8834bb1d8ee5dc048ee3124f2c7c1afcc6bc9aed03f11e9dfd8c69470a5db340" 185 | dependencies = [ 186 | "feature-probe", 187 | "serde", 188 | ] 189 | 190 | [[package]] 191 | name = "byte-tools" 192 | version = "0.2.0" 193 | source = "registry+https://github.com/rust-lang/crates.io-index" 194 | checksum = "560c32574a12a89ecd91f5e742165893f86e3ab98d21f8ea548658eb9eef5f40" 195 | 196 | [[package]] 197 | name = "byte-tools" 198 | version = "0.3.1" 199 | source = "registry+https://github.com/rust-lang/crates.io-index" 200 | checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" 201 | 202 | [[package]] 203 | name = "byteorder" 204 | version = "1.3.4" 205 | source = "registry+https://github.com/rust-lang/crates.io-index" 206 | checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de" 207 | 208 | [[package]] 209 | name = "bytes" 210 | version = "0.4.12" 211 | source = "registry+https://github.com/rust-lang/crates.io-index" 212 | checksum = "206fdffcfa2df7cbe15601ef46c813fce0965eb3286db6b56c583b814b51c81c" 213 | dependencies = [ 214 | "byteorder", 215 | "either", 216 | "iovec", 217 | ] 218 | 219 | [[package]] 220 | name = "bytes" 221 | version = "0.5.4" 222 | source = "registry+https://github.com/rust-lang/crates.io-index" 223 | checksum = "130aac562c0dd69c56b3b1cc8ffd2e17be31d0b6c25b61c96b76231aa23e39e1" 224 | 225 | [[package]] 226 | name = "cc" 227 | version = "1.0.49" 228 | source = "registry+https://github.com/rust-lang/crates.io-index" 229 | checksum = "e450b8da92aa6f274e7c6437692f9f2ce6d701fb73bacfcf87897b3f89a4c20e" 230 | dependencies = [ 231 | "jobserver", 232 | "num_cpus", 233 | ] 234 | 235 | [[package]] 236 | name = "cfg-if" 237 | version = "0.1.10" 238 | source = "registry+https://github.com/rust-lang/crates.io-index" 239 | checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" 240 | 241 | [[package]] 242 | name = "chrono" 243 | version = "0.4.11" 244 | source = "registry+https://github.com/rust-lang/crates.io-index" 245 | checksum = "80094f509cf8b5ae86a4966a39b3ff66cd7e2a3e594accec3743ff3fabeab5b2" 246 | dependencies = [ 247 | "num-integer", 248 | "num-traits 0.2.11", 249 | "time", 250 | ] 251 | 252 | [[package]] 253 | name = "clap" 254 | version = "2.33.0" 255 | source = "registry+https://github.com/rust-lang/crates.io-index" 256 | checksum = "5067f5bb2d80ef5d68b4c87db81601f0b75bca627bc2ef76b141d7b846a3c6d9" 257 | dependencies = [ 258 | "ansi_term", 259 | "atty", 260 | "bitflags", 261 | "strsim", 262 | "textwrap", 263 | "unicode-width", 264 | "vec_map", 265 | ] 266 | 267 | [[package]] 268 | name = "clear_on_drop" 269 | version = "0.2.3" 270 | source = "registry+https://github.com/rust-lang/crates.io-index" 271 | checksum = "97276801e127ffb46b66ce23f35cc96bd454fa311294bced4bbace7baa8b1d17" 272 | dependencies = [ 273 | "cc", 274 | ] 275 | 276 | [[package]] 277 | name = "cloudabi" 278 | version = "0.0.3" 279 | source = "registry+https://github.com/rust-lang/crates.io-index" 280 | checksum = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" 281 | dependencies = [ 282 | "bitflags", 283 | ] 284 | 285 | [[package]] 286 | name = "colored" 287 | version = "1.9.3" 288 | source = "registry+https://github.com/rust-lang/crates.io-index" 289 | checksum = "f4ffc801dacf156c5854b9df4f425a626539c3a6ef7893cc0c5084a23f0b6c59" 290 | dependencies = [ 291 | "atty", 292 | "lazy_static", 293 | "winapi 0.3.8", 294 | ] 295 | 296 | [[package]] 297 | name = "combine" 298 | version = "2.5.2" 299 | source = "registry+https://github.com/rust-lang/crates.io-index" 300 | checksum = "1645a65a99c7c8d345761f4b75a6ffe5be3b3b27a93ee731fccc5050ba6be97c" 301 | dependencies = [ 302 | "ascii", 303 | "byteorder", 304 | ] 305 | 306 | [[package]] 307 | name = "core-foundation" 308 | version = "0.7.0" 309 | source = "registry+https://github.com/rust-lang/crates.io-index" 310 | checksum = "57d24c7a13c43e870e37c1556b74555437870a04514f7685f5b354e090567171" 311 | dependencies = [ 312 | "core-foundation-sys", 313 | "libc", 314 | ] 315 | 316 | [[package]] 317 | name = "core-foundation-sys" 318 | version = "0.7.0" 319 | source = "registry+https://github.com/rust-lang/crates.io-index" 320 | checksum = "b3a71ab494c0b5b860bdc8407ae08978052417070c2ced38573a9157ad75b8ac" 321 | 322 | [[package]] 323 | name = "crossbeam-deque" 324 | version = "0.7.3" 325 | source = "registry+https://github.com/rust-lang/crates.io-index" 326 | checksum = "9f02af974daeee82218205558e51ec8768b48cf524bd01d550abe5573a608285" 327 | dependencies = [ 328 | "crossbeam-epoch", 329 | "crossbeam-utils", 330 | "maybe-uninit", 331 | ] 332 | 333 | [[package]] 334 | name = "crossbeam-epoch" 335 | version = "0.8.2" 336 | source = "registry+https://github.com/rust-lang/crates.io-index" 337 | checksum = "058ed274caafc1f60c4997b5fc07bf7dc7cca454af7c6e81edffe5f33f70dace" 338 | dependencies = [ 339 | "autocfg 1.0.0", 340 | "cfg-if", 341 | "crossbeam-utils", 342 | "lazy_static", 343 | "maybe-uninit", 344 | "memoffset", 345 | "scopeguard", 346 | ] 347 | 348 | [[package]] 349 | name = "crossbeam-queue" 350 | version = "0.2.1" 351 | source = "registry+https://github.com/rust-lang/crates.io-index" 352 | checksum = "c695eeca1e7173472a32221542ae469b3e9aac3a4fc81f7696bcad82029493db" 353 | dependencies = [ 354 | "cfg-if", 355 | "crossbeam-utils", 356 | ] 357 | 358 | [[package]] 359 | name = "crossbeam-utils" 360 | version = "0.7.2" 361 | source = "registry+https://github.com/rust-lang/crates.io-index" 362 | checksum = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8" 363 | dependencies = [ 364 | "autocfg 1.0.0", 365 | "cfg-if", 366 | "lazy_static", 367 | ] 368 | 369 | [[package]] 370 | name = "crypto-mac" 371 | version = "0.7.0" 372 | source = "registry+https://github.com/rust-lang/crates.io-index" 373 | checksum = "4434400df11d95d556bac068ddfedd482915eb18fe8bea89bc80b6e4b1c179e5" 374 | dependencies = [ 375 | "generic-array 0.12.3", 376 | "subtle 1.0.0", 377 | ] 378 | 379 | [[package]] 380 | name = "ct-logs" 381 | version = "0.6.0" 382 | source = "registry+https://github.com/rust-lang/crates.io-index" 383 | checksum = "4d3686f5fa27dbc1d76c751300376e167c5a43387f44bb451fd1c24776e49113" 384 | dependencies = [ 385 | "sct", 386 | ] 387 | 388 | [[package]] 389 | name = "curve25519-dalek" 390 | version = "1.2.3" 391 | source = "registry+https://github.com/rust-lang/crates.io-index" 392 | checksum = "8b7dcd30ba50cdf88b55b033456138b7c0ac4afdc436d82e1b79f370f24cc66d" 393 | dependencies = [ 394 | "byteorder", 395 | "clear_on_drop", 396 | "digest 0.8.1", 397 | "rand_core 0.3.1", 398 | "subtle 2.2.2", 399 | ] 400 | 401 | [[package]] 402 | name = "digest" 403 | version = "0.7.6" 404 | source = "registry+https://github.com/rust-lang/crates.io-index" 405 | checksum = "03b072242a8cbaf9c145665af9d250c59af3b958f83ed6824e13533cf76d5b90" 406 | dependencies = [ 407 | "generic-array 0.9.0", 408 | ] 409 | 410 | [[package]] 411 | name = "digest" 412 | version = "0.8.1" 413 | source = "registry+https://github.com/rust-lang/crates.io-index" 414 | checksum = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5" 415 | dependencies = [ 416 | "generic-array 0.12.3", 417 | ] 418 | 419 | [[package]] 420 | name = "dtoa" 421 | version = "0.4.5" 422 | source = "registry+https://github.com/rust-lang/crates.io-index" 423 | checksum = "4358a9e11b9a09cf52383b451b49a169e8d797b68aa02301ff586d70d9661ea3" 424 | 425 | [[package]] 426 | name = "ed25519-dalek" 427 | version = "1.0.0-pre.1" 428 | source = "registry+https://github.com/rust-lang/crates.io-index" 429 | checksum = "81956bcf7ef761fb4e1d88de3fa181358a0d26cbcb9755b587a08f9119824b86" 430 | dependencies = [ 431 | "clear_on_drop", 432 | "curve25519-dalek", 433 | "failure", 434 | "rand 0.6.5", 435 | "serde", 436 | "sha2 0.8.1", 437 | ] 438 | 439 | [[package]] 440 | name = "either" 441 | version = "1.5.3" 442 | source = "registry+https://github.com/rust-lang/crates.io-index" 443 | checksum = "bb1f6b1ce1c140482ea30ddd3335fc0024ac7ee112895426e0a629a6c20adfe3" 444 | 445 | [[package]] 446 | name = "elfkit" 447 | version = "0.0.6" 448 | source = "registry+https://github.com/rust-lang/crates.io-index" 449 | checksum = "02f182eda16a7360c80a2f8638d0726e9d5478173058f1505c42536ca666ecd2" 450 | dependencies = [ 451 | "ar", 452 | "bit-vec 0.5.1", 453 | "bitflags", 454 | "bloom", 455 | "byteorder", 456 | "clap", 457 | "colored", 458 | "enum-primitive-derive", 459 | "env_logger 0.5.13", 460 | "fnv", 461 | "glob", 462 | "indexmap", 463 | "itertools 0.7.11", 464 | "log", 465 | "num-traits 0.2.11", 466 | "pretty_env_logger", 467 | "rayon", 468 | "sha2 0.7.1", 469 | "tempfile", 470 | ] 471 | 472 | [[package]] 473 | name = "encoding_rs" 474 | version = "0.8.22" 475 | source = "registry+https://github.com/rust-lang/crates.io-index" 476 | checksum = "cd8d03faa7fe0c1431609dfad7bbe827af30f82e1e2ae6f7ee4fca6bd764bc28" 477 | dependencies = [ 478 | "cfg-if", 479 | ] 480 | 481 | [[package]] 482 | name = "enum-primitive-derive" 483 | version = "0.1.2" 484 | source = "registry+https://github.com/rust-lang/crates.io-index" 485 | checksum = "e2b90e520ec62c1864c8c78d637acbfe8baf5f63240f2fb8165b8325c07812dd" 486 | dependencies = [ 487 | "num-traits 0.1.43", 488 | "quote 0.3.15", 489 | "syn 0.11.11", 490 | ] 491 | 492 | [[package]] 493 | name = "env_logger" 494 | version = "0.5.13" 495 | source = "registry+https://github.com/rust-lang/crates.io-index" 496 | checksum = "15b0a4d2e39f8420210be8b27eeda28029729e2fd4291019455016c348240c38" 497 | dependencies = [ 498 | "atty", 499 | "humantime", 500 | "log", 501 | "regex", 502 | "termcolor", 503 | ] 504 | 505 | [[package]] 506 | name = "env_logger" 507 | version = "0.7.1" 508 | source = "registry+https://github.com/rust-lang/crates.io-index" 509 | checksum = "44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36" 510 | dependencies = [ 511 | "atty", 512 | "humantime", 513 | "log", 514 | "regex", 515 | "termcolor", 516 | ] 517 | 518 | [[package]] 519 | name = "failure" 520 | version = "0.1.8" 521 | source = "registry+https://github.com/rust-lang/crates.io-index" 522 | checksum = "d32e9bd16cc02eae7db7ef620b392808b89f6a5e16bb3497d159c6b92a0f4f86" 523 | dependencies = [ 524 | "backtrace", 525 | "failure_derive", 526 | ] 527 | 528 | [[package]] 529 | name = "failure_derive" 530 | version = "0.1.8" 531 | source = "registry+https://github.com/rust-lang/crates.io-index" 532 | checksum = "aa4da3c766cd7a0db8242e326e9e4e081edd567072893ed320008189715366a4" 533 | dependencies = [ 534 | "proc-macro2 1.0.12", 535 | "quote 1.0.4", 536 | "syn 1.0.19", 537 | "synstructure", 538 | ] 539 | 540 | [[package]] 541 | name = "fake-simd" 542 | version = "0.1.2" 543 | source = "registry+https://github.com/rust-lang/crates.io-index" 544 | checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" 545 | 546 | [[package]] 547 | name = "feature-probe" 548 | version = "0.1.1" 549 | source = "registry+https://github.com/rust-lang/crates.io-index" 550 | checksum = "835a3dc7d1ec9e75e2b5fb4ba75396837112d2060b03f7d43bc1897c7f7211da" 551 | 552 | [[package]] 553 | name = "fnv" 554 | version = "1.0.6" 555 | source = "registry+https://github.com/rust-lang/crates.io-index" 556 | checksum = "2fad85553e09a6f881f739c29f0b00b0f01357c743266d478b68951ce23285f3" 557 | 558 | [[package]] 559 | name = "fuchsia-cprng" 560 | version = "0.1.1" 561 | source = "registry+https://github.com/rust-lang/crates.io-index" 562 | checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" 563 | 564 | [[package]] 565 | name = "fuchsia-zircon" 566 | version = "0.3.3" 567 | source = "registry+https://github.com/rust-lang/crates.io-index" 568 | checksum = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" 569 | dependencies = [ 570 | "bitflags", 571 | "fuchsia-zircon-sys", 572 | ] 573 | 574 | [[package]] 575 | name = "fuchsia-zircon-sys" 576 | version = "0.3.3" 577 | source = "registry+https://github.com/rust-lang/crates.io-index" 578 | checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" 579 | 580 | [[package]] 581 | name = "futures" 582 | version = "0.1.29" 583 | source = "registry+https://github.com/rust-lang/crates.io-index" 584 | checksum = "1b980f2816d6ee8673b6517b52cb0e808a180efc92e5c19d02cdda79066703ef" 585 | 586 | [[package]] 587 | name = "futures-channel" 588 | version = "0.3.4" 589 | source = "registry+https://github.com/rust-lang/crates.io-index" 590 | checksum = "f0c77d04ce8edd9cb903932b608268b3fffec4163dc053b3b402bf47eac1f1a8" 591 | dependencies = [ 592 | "futures-core", 593 | ] 594 | 595 | [[package]] 596 | name = "futures-core" 597 | version = "0.3.4" 598 | source = "registry+https://github.com/rust-lang/crates.io-index" 599 | checksum = "f25592f769825e89b92358db00d26f965761e094951ac44d3663ef25b7ac464a" 600 | 601 | [[package]] 602 | name = "futures-io" 603 | version = "0.3.4" 604 | source = "registry+https://github.com/rust-lang/crates.io-index" 605 | checksum = "a638959aa96152c7a4cddf50fcb1e3fede0583b27157c26e67d6f99904090dc6" 606 | 607 | [[package]] 608 | name = "futures-macro" 609 | version = "0.3.4" 610 | source = "registry+https://github.com/rust-lang/crates.io-index" 611 | checksum = "9a5081aa3de1f7542a794a397cde100ed903b0630152d0973479018fd85423a7" 612 | dependencies = [ 613 | "proc-macro-hack", 614 | "proc-macro2 1.0.12", 615 | "quote 1.0.4", 616 | "syn 1.0.19", 617 | ] 618 | 619 | [[package]] 620 | name = "futures-sink" 621 | version = "0.3.4" 622 | source = "registry+https://github.com/rust-lang/crates.io-index" 623 | checksum = "3466821b4bc114d95b087b850a724c6f83115e929bc88f1fa98a3304a944c8a6" 624 | 625 | [[package]] 626 | name = "futures-task" 627 | version = "0.3.4" 628 | source = "registry+https://github.com/rust-lang/crates.io-index" 629 | checksum = "7b0a34e53cf6cdcd0178aa573aed466b646eb3db769570841fda0c7ede375a27" 630 | 631 | [[package]] 632 | name = "futures-util" 633 | version = "0.3.4" 634 | source = "registry+https://github.com/rust-lang/crates.io-index" 635 | checksum = "22766cf25d64306bedf0384da004d05c9974ab104fcc4528f1236181c18004c5" 636 | dependencies = [ 637 | "futures-core", 638 | "futures-io", 639 | "futures-macro", 640 | "futures-task", 641 | "memchr", 642 | "pin-utils", 643 | "proc-macro-hack", 644 | "proc-macro-nested", 645 | "slab", 646 | ] 647 | 648 | [[package]] 649 | name = "generic-array" 650 | version = "0.9.0" 651 | source = "registry+https://github.com/rust-lang/crates.io-index" 652 | checksum = "ef25c5683767570c2bbd7deba372926a55eaae9982d7726ee2a1050239d45b9d" 653 | dependencies = [ 654 | "typenum", 655 | ] 656 | 657 | [[package]] 658 | name = "generic-array" 659 | version = "0.12.3" 660 | source = "registry+https://github.com/rust-lang/crates.io-index" 661 | checksum = "c68f0274ae0e023facc3c97b2e00f076be70e254bc851d972503b328db79b2ec" 662 | dependencies = [ 663 | "typenum", 664 | ] 665 | 666 | [[package]] 667 | name = "generic-array" 668 | version = "0.13.2" 669 | source = "registry+https://github.com/rust-lang/crates.io-index" 670 | checksum = "0ed1e761351b56f54eb9dcd0cfaca9fd0daecf93918e1cfc01c8a3d26ee7adcd" 671 | dependencies = [ 672 | "serde", 673 | "typenum", 674 | ] 675 | 676 | [[package]] 677 | name = "getrandom" 678 | version = "0.1.14" 679 | source = "registry+https://github.com/rust-lang/crates.io-index" 680 | checksum = "7abc8dd8451921606d809ba32e95b6111925cd2906060d2dcc29c070220503eb" 681 | dependencies = [ 682 | "cfg-if", 683 | "libc", 684 | "wasi", 685 | ] 686 | 687 | [[package]] 688 | name = "glob" 689 | version = "0.2.11" 690 | source = "registry+https://github.com/rust-lang/crates.io-index" 691 | checksum = "8be18de09a56b60ed0edf84bc9df007e30040691af7acd1c41874faac5895bfb" 692 | 693 | [[package]] 694 | name = "h2" 695 | version = "0.2.5" 696 | source = "registry+https://github.com/rust-lang/crates.io-index" 697 | checksum = "79b7246d7e4b979c03fa093da39cfb3617a96bbeee6310af63991668d7e843ff" 698 | dependencies = [ 699 | "bytes 0.5.4", 700 | "fnv", 701 | "futures-core", 702 | "futures-sink", 703 | "futures-util", 704 | "http", 705 | "indexmap", 706 | "log", 707 | "slab", 708 | "tokio 0.2.20", 709 | "tokio-util", 710 | ] 711 | 712 | [[package]] 713 | name = "hash32" 714 | version = "0.1.1" 715 | source = "registry+https://github.com/rust-lang/crates.io-index" 716 | checksum = "d4041af86e63ac4298ce40e5cca669066e75b6f1aa3390fe2561ffa5e1d9f4cc" 717 | dependencies = [ 718 | "byteorder", 719 | ] 720 | 721 | [[package]] 722 | name = "hermit-abi" 723 | version = "0.1.12" 724 | source = "registry+https://github.com/rust-lang/crates.io-index" 725 | checksum = "61565ff7aaace3525556587bd2dc31d4a07071957be715e63ce7b1eccf51a8f4" 726 | dependencies = [ 727 | "libc", 728 | ] 729 | 730 | [[package]] 731 | name = "hex" 732 | version = "0.4.2" 733 | source = "registry+https://github.com/rust-lang/crates.io-index" 734 | checksum = "644f9158b2f133fd50f5fb3242878846d9eb792e445c893805ff0e3824006e35" 735 | 736 | [[package]] 737 | name = "hmac" 738 | version = "0.7.1" 739 | source = "registry+https://github.com/rust-lang/crates.io-index" 740 | checksum = "5dcb5e64cda4c23119ab41ba960d1e170a774c8e4b9d9e6a9bc18aabf5e59695" 741 | dependencies = [ 742 | "crypto-mac", 743 | "digest 0.8.1", 744 | ] 745 | 746 | [[package]] 747 | name = "http" 748 | version = "0.2.1" 749 | source = "registry+https://github.com/rust-lang/crates.io-index" 750 | checksum = "28d569972648b2c512421b5f2a405ad6ac9666547189d0c5477a3f200f3e02f9" 751 | dependencies = [ 752 | "bytes 0.5.4", 753 | "fnv", 754 | "itoa", 755 | ] 756 | 757 | [[package]] 758 | name = "http-body" 759 | version = "0.3.1" 760 | source = "registry+https://github.com/rust-lang/crates.io-index" 761 | checksum = "13d5ff830006f7646652e057693569bfe0d51760c0085a071769d142a205111b" 762 | dependencies = [ 763 | "bytes 0.5.4", 764 | "http", 765 | ] 766 | 767 | [[package]] 768 | name = "httparse" 769 | version = "1.3.4" 770 | source = "registry+https://github.com/rust-lang/crates.io-index" 771 | checksum = "cd179ae861f0c2e53da70d892f5f3029f9594be0c41dc5269cd371691b1dc2f9" 772 | 773 | [[package]] 774 | name = "humantime" 775 | version = "1.3.0" 776 | source = "registry+https://github.com/rust-lang/crates.io-index" 777 | checksum = "df004cfca50ef23c36850aaaa59ad52cc70d0e90243c3c7737a4dd32dc7a3c4f" 778 | dependencies = [ 779 | "quick-error", 780 | ] 781 | 782 | [[package]] 783 | name = "hyper" 784 | version = "0.13.5" 785 | source = "registry+https://github.com/rust-lang/crates.io-index" 786 | checksum = "96816e1d921eca64d208a85aab4f7798455a8e34229ee5a88c935bdee1b78b14" 787 | dependencies = [ 788 | "bytes 0.5.4", 789 | "futures-channel", 790 | "futures-core", 791 | "futures-util", 792 | "h2", 793 | "http", 794 | "http-body", 795 | "httparse", 796 | "itoa", 797 | "log", 798 | "net2", 799 | "pin-project", 800 | "time", 801 | "tokio 0.2.20", 802 | "tower-service", 803 | "want", 804 | ] 805 | 806 | [[package]] 807 | name = "hyper-rustls" 808 | version = "0.20.0" 809 | source = "registry+https://github.com/rust-lang/crates.io-index" 810 | checksum = "ac965ea399ec3a25ac7d13b8affd4b8f39325cca00858ddf5eb29b79e6b14b08" 811 | dependencies = [ 812 | "bytes 0.5.4", 813 | "ct-logs", 814 | "futures-util", 815 | "hyper", 816 | "log", 817 | "rustls", 818 | "rustls-native-certs", 819 | "tokio 0.2.20", 820 | "tokio-rustls", 821 | "webpki", 822 | ] 823 | 824 | [[package]] 825 | name = "idna" 826 | version = "0.2.0" 827 | source = "registry+https://github.com/rust-lang/crates.io-index" 828 | checksum = "02e2673c30ee86b5b96a9cb52ad15718aa1f966f5ab9ad54a8b95d5ca33120a9" 829 | dependencies = [ 830 | "matches", 831 | "unicode-bidi", 832 | "unicode-normalization", 833 | ] 834 | 835 | [[package]] 836 | name = "indexmap" 837 | version = "1.3.2" 838 | source = "registry+https://github.com/rust-lang/crates.io-index" 839 | checksum = "076f042c5b7b98f31d205f1249267e12a6518c1481e9dae9764af19b707d2292" 840 | dependencies = [ 841 | "autocfg 1.0.0", 842 | ] 843 | 844 | [[package]] 845 | name = "iovec" 846 | version = "0.1.4" 847 | source = "registry+https://github.com/rust-lang/crates.io-index" 848 | checksum = "b2b3ea6ff95e175473f8ffe6a7eb7c00d054240321b84c57051175fe3c1e075e" 849 | dependencies = [ 850 | "libc", 851 | ] 852 | 853 | [[package]] 854 | name = "itertools" 855 | version = "0.7.11" 856 | source = "registry+https://github.com/rust-lang/crates.io-index" 857 | checksum = "0d47946d458e94a1b7bcabbf6521ea7c037062c81f534615abcad76e84d4970d" 858 | dependencies = [ 859 | "either", 860 | ] 861 | 862 | [[package]] 863 | name = "itertools" 864 | version = "0.8.2" 865 | source = "registry+https://github.com/rust-lang/crates.io-index" 866 | checksum = "f56a2d0bc861f9165be4eb3442afd3c236d8a98afd426f65d92324ae1091a484" 867 | dependencies = [ 868 | "either", 869 | ] 870 | 871 | [[package]] 872 | name = "itoa" 873 | version = "0.4.5" 874 | source = "registry+https://github.com/rust-lang/crates.io-index" 875 | checksum = "b8b7a7c0c47db5545ed3fef7468ee7bb5b74691498139e4b3f6a20685dc6dd8e" 876 | 877 | [[package]] 878 | name = "jobserver" 879 | version = "0.1.21" 880 | source = "registry+https://github.com/rust-lang/crates.io-index" 881 | checksum = "5c71313ebb9439f74b00d9d2dcec36440beaf57a6aa0623068441dd7cd81a7f2" 882 | dependencies = [ 883 | "libc", 884 | ] 885 | 886 | [[package]] 887 | name = "js-sys" 888 | version = "0.3.39" 889 | source = "registry+https://github.com/rust-lang/crates.io-index" 890 | checksum = "fa5a448de267e7358beaf4a5d849518fe9a0c13fce7afd44b06e68550e5562a7" 891 | dependencies = [ 892 | "wasm-bindgen", 893 | ] 894 | 895 | [[package]] 896 | name = "kernel32-sys" 897 | version = "0.2.2" 898 | source = "registry+https://github.com/rust-lang/crates.io-index" 899 | checksum = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" 900 | dependencies = [ 901 | "winapi 0.2.8", 902 | "winapi-build", 903 | ] 904 | 905 | [[package]] 906 | name = "lazy_static" 907 | version = "1.4.0" 908 | source = "registry+https://github.com/rust-lang/crates.io-index" 909 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 910 | dependencies = [ 911 | "spin", 912 | ] 913 | 914 | [[package]] 915 | name = "libc" 916 | version = "0.2.69" 917 | source = "registry+https://github.com/rust-lang/crates.io-index" 918 | checksum = "99e85c08494b21a9054e7fe1374a732aeadaff3980b6990b94bfd3a70f690005" 919 | 920 | [[package]] 921 | name = "lock_api" 922 | version = "0.3.4" 923 | source = "registry+https://github.com/rust-lang/crates.io-index" 924 | checksum = "c4da24a77a3d8a6d4862d95f72e6fdb9c09a643ecdb402d754004a557f2bec75" 925 | dependencies = [ 926 | "scopeguard", 927 | ] 928 | 929 | [[package]] 930 | name = "log" 931 | version = "0.4.8" 932 | source = "registry+https://github.com/rust-lang/crates.io-index" 933 | checksum = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7" 934 | dependencies = [ 935 | "cfg-if", 936 | ] 937 | 938 | [[package]] 939 | name = "matches" 940 | version = "0.1.8" 941 | source = "registry+https://github.com/rust-lang/crates.io-index" 942 | checksum = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" 943 | 944 | [[package]] 945 | name = "maybe-uninit" 946 | version = "2.0.0" 947 | source = "registry+https://github.com/rust-lang/crates.io-index" 948 | checksum = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00" 949 | 950 | [[package]] 951 | name = "memchr" 952 | version = "2.3.3" 953 | source = "registry+https://github.com/rust-lang/crates.io-index" 954 | checksum = "3728d817d99e5ac407411fa471ff9800a778d88a24685968b36824eaf4bee400" 955 | 956 | [[package]] 957 | name = "memmap" 958 | version = "0.7.0" 959 | source = "registry+https://github.com/rust-lang/crates.io-index" 960 | checksum = "6585fd95e7bb50d6cc31e20d4cf9afb4e2ba16c5846fc76793f11218da9c475b" 961 | dependencies = [ 962 | "libc", 963 | "winapi 0.3.8", 964 | ] 965 | 966 | [[package]] 967 | name = "memoffset" 968 | version = "0.5.4" 969 | source = "registry+https://github.com/rust-lang/crates.io-index" 970 | checksum = "b4fc2c02a7e374099d4ee95a193111f72d2110197fe200272371758f6c3643d8" 971 | dependencies = [ 972 | "autocfg 1.0.0", 973 | ] 974 | 975 | [[package]] 976 | name = "mime" 977 | version = "0.3.16" 978 | source = "registry+https://github.com/rust-lang/crates.io-index" 979 | checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" 980 | 981 | [[package]] 982 | name = "mime_guess" 983 | version = "2.0.3" 984 | source = "registry+https://github.com/rust-lang/crates.io-index" 985 | checksum = "2684d4c2e97d99848d30b324b00c8fcc7e5c897b7cbb5819b09e7c90e8baf212" 986 | dependencies = [ 987 | "mime", 988 | "unicase", 989 | ] 990 | 991 | [[package]] 992 | name = "mio" 993 | version = "0.6.22" 994 | source = "registry+https://github.com/rust-lang/crates.io-index" 995 | checksum = "fce347092656428bc8eaf6201042cb551b8d67855af7374542a92a0fbfcac430" 996 | dependencies = [ 997 | "cfg-if", 998 | "fuchsia-zircon", 999 | "fuchsia-zircon-sys", 1000 | "iovec", 1001 | "kernel32-sys", 1002 | "libc", 1003 | "log", 1004 | "miow", 1005 | "net2", 1006 | "slab", 1007 | "winapi 0.2.8", 1008 | ] 1009 | 1010 | [[package]] 1011 | name = "mio-uds" 1012 | version = "0.6.8" 1013 | source = "registry+https://github.com/rust-lang/crates.io-index" 1014 | checksum = "afcb699eb26d4332647cc848492bbc15eafb26f08d0304550d5aa1f612e066f0" 1015 | dependencies = [ 1016 | "iovec", 1017 | "libc", 1018 | "mio", 1019 | ] 1020 | 1021 | [[package]] 1022 | name = "miow" 1023 | version = "0.2.1" 1024 | source = "registry+https://github.com/rust-lang/crates.io-index" 1025 | checksum = "8c1f2f3b1cf331de6896aabf6e9d55dca90356cc9960cca7eaaf408a355ae919" 1026 | dependencies = [ 1027 | "kernel32-sys", 1028 | "net2", 1029 | "winapi 0.2.8", 1030 | "ws2_32-sys", 1031 | ] 1032 | 1033 | [[package]] 1034 | name = "net2" 1035 | version = "0.2.34" 1036 | source = "registry+https://github.com/rust-lang/crates.io-index" 1037 | checksum = "2ba7c918ac76704fb42afcbbb43891e72731f3dcca3bef2a19786297baf14af7" 1038 | dependencies = [ 1039 | "cfg-if", 1040 | "libc", 1041 | "winapi 0.3.8", 1042 | ] 1043 | 1044 | [[package]] 1045 | name = "num-derive" 1046 | version = "0.2.5" 1047 | source = "registry+https://github.com/rust-lang/crates.io-index" 1048 | checksum = "eafd0b45c5537c3ba526f79d3e75120036502bebacbb3f3220914067ce39dbf2" 1049 | dependencies = [ 1050 | "proc-macro2 0.4.30", 1051 | "quote 0.6.13", 1052 | "syn 0.15.44", 1053 | ] 1054 | 1055 | [[package]] 1056 | name = "num-derive" 1057 | version = "0.3.0" 1058 | source = "registry+https://github.com/rust-lang/crates.io-index" 1059 | checksum = "0c8b15b261814f992e33760b1fca9fe8b693d8a65299f20c9901688636cfb746" 1060 | dependencies = [ 1061 | "proc-macro2 1.0.12", 1062 | "quote 1.0.4", 1063 | "syn 1.0.19", 1064 | ] 1065 | 1066 | [[package]] 1067 | name = "num-integer" 1068 | version = "0.1.42" 1069 | source = "registry+https://github.com/rust-lang/crates.io-index" 1070 | checksum = "3f6ea62e9d81a77cd3ee9a2a5b9b609447857f3d358704331e4ef39eb247fcba" 1071 | dependencies = [ 1072 | "autocfg 1.0.0", 1073 | "num-traits 0.2.11", 1074 | ] 1075 | 1076 | [[package]] 1077 | name = "num-traits" 1078 | version = "0.1.43" 1079 | source = "registry+https://github.com/rust-lang/crates.io-index" 1080 | checksum = "92e5113e9fd4cc14ded8e499429f396a20f98c772a47cc8622a736e1ec843c31" 1081 | dependencies = [ 1082 | "num-traits 0.2.11", 1083 | ] 1084 | 1085 | [[package]] 1086 | name = "num-traits" 1087 | version = "0.2.11" 1088 | source = "registry+https://github.com/rust-lang/crates.io-index" 1089 | checksum = "c62be47e61d1842b9170f0fdeec8eba98e60e90e5446449a0545e5152acd7096" 1090 | dependencies = [ 1091 | "autocfg 1.0.0", 1092 | ] 1093 | 1094 | [[package]] 1095 | name = "num_cpus" 1096 | version = "1.13.0" 1097 | source = "registry+https://github.com/rust-lang/crates.io-index" 1098 | checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3" 1099 | dependencies = [ 1100 | "hermit-abi", 1101 | "libc", 1102 | ] 1103 | 1104 | [[package]] 1105 | name = "opaque-debug" 1106 | version = "0.2.3" 1107 | source = "registry+https://github.com/rust-lang/crates.io-index" 1108 | checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" 1109 | 1110 | [[package]] 1111 | name = "openssl-probe" 1112 | version = "0.1.2" 1113 | source = "registry+https://github.com/rust-lang/crates.io-index" 1114 | checksum = "77af24da69f9d9341038eba93a073b1fdaaa1b788221b00a69bce9e762cb32de" 1115 | 1116 | [[package]] 1117 | name = "parking_lot" 1118 | version = "0.9.0" 1119 | source = "registry+https://github.com/rust-lang/crates.io-index" 1120 | checksum = "f842b1982eb6c2fe34036a4fbfb06dd185a3f5c8edfaacdf7d1ea10b07de6252" 1121 | dependencies = [ 1122 | "lock_api", 1123 | "parking_lot_core", 1124 | "rustc_version", 1125 | ] 1126 | 1127 | [[package]] 1128 | name = "parking_lot_core" 1129 | version = "0.6.2" 1130 | source = "registry+https://github.com/rust-lang/crates.io-index" 1131 | checksum = "b876b1b9e7ac6e1a74a6da34d25c42e17e8862aa409cbbbdcfc8d86c6f3bc62b" 1132 | dependencies = [ 1133 | "cfg-if", 1134 | "cloudabi", 1135 | "libc", 1136 | "redox_syscall", 1137 | "rustc_version", 1138 | "smallvec 0.6.13", 1139 | "winapi 0.3.8", 1140 | ] 1141 | 1142 | [[package]] 1143 | name = "pbkdf2" 1144 | version = "0.3.0" 1145 | source = "registry+https://github.com/rust-lang/crates.io-index" 1146 | checksum = "006c038a43a45995a9670da19e67600114740e8511d4333bf97a56e66a7542d9" 1147 | dependencies = [ 1148 | "byteorder", 1149 | "crypto-mac", 1150 | ] 1151 | 1152 | [[package]] 1153 | name = "percent-encoding" 1154 | version = "2.1.0" 1155 | source = "registry+https://github.com/rust-lang/crates.io-index" 1156 | checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" 1157 | 1158 | [[package]] 1159 | name = "pin-project" 1160 | version = "0.4.13" 1161 | source = "registry+https://github.com/rust-lang/crates.io-index" 1162 | checksum = "82c3bfbfb5bb42f99498c7234bbd768c220eb0cea6818259d0d18a1aa3d2595d" 1163 | dependencies = [ 1164 | "pin-project-internal", 1165 | ] 1166 | 1167 | [[package]] 1168 | name = "pin-project-internal" 1169 | version = "0.4.13" 1170 | source = "registry+https://github.com/rust-lang/crates.io-index" 1171 | checksum = "ccbf6449dcfb18562c015526b085b8df1aa3cdab180af8ec2ebd300a3bd28f63" 1172 | dependencies = [ 1173 | "proc-macro2 1.0.12", 1174 | "quote 1.0.4", 1175 | "syn 1.0.19", 1176 | ] 1177 | 1178 | [[package]] 1179 | name = "pin-project-lite" 1180 | version = "0.1.5" 1181 | source = "registry+https://github.com/rust-lang/crates.io-index" 1182 | checksum = "f7505eeebd78492e0f6108f7171c4948dbb120ee8119d9d77d0afa5469bef67f" 1183 | 1184 | [[package]] 1185 | name = "pin-utils" 1186 | version = "0.1.0" 1187 | source = "registry+https://github.com/rust-lang/crates.io-index" 1188 | checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" 1189 | 1190 | [[package]] 1191 | name = "ppv-lite86" 1192 | version = "0.2.6" 1193 | source = "registry+https://github.com/rust-lang/crates.io-index" 1194 | checksum = "74490b50b9fbe561ac330df47c08f3f33073d2d00c150f719147d7c54522fa1b" 1195 | 1196 | [[package]] 1197 | name = "pretty_env_logger" 1198 | version = "0.2.5" 1199 | source = "registry+https://github.com/rust-lang/crates.io-index" 1200 | checksum = "ed8d1e63042e889b85228620629b51c011d380eed2c7e0015f8a644def280c28" 1201 | dependencies = [ 1202 | "ansi_term", 1203 | "chrono", 1204 | "env_logger 0.5.13", 1205 | "log", 1206 | ] 1207 | 1208 | [[package]] 1209 | name = "proc-macro-hack" 1210 | version = "0.5.15" 1211 | source = "registry+https://github.com/rust-lang/crates.io-index" 1212 | checksum = "0d659fe7c6d27f25e9d80a1a094c223f5246f6a6596453e09d7229bf42750b63" 1213 | 1214 | [[package]] 1215 | name = "proc-macro-nested" 1216 | version = "0.1.4" 1217 | source = "registry+https://github.com/rust-lang/crates.io-index" 1218 | checksum = "8e946095f9d3ed29ec38de908c22f95d9ac008e424c7bcae54c75a79c527c694" 1219 | 1220 | [[package]] 1221 | name = "proc-macro2" 1222 | version = "0.4.30" 1223 | source = "registry+https://github.com/rust-lang/crates.io-index" 1224 | checksum = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759" 1225 | dependencies = [ 1226 | "unicode-xid 0.1.0", 1227 | ] 1228 | 1229 | [[package]] 1230 | name = "proc-macro2" 1231 | version = "1.0.12" 1232 | source = "registry+https://github.com/rust-lang/crates.io-index" 1233 | checksum = "8872cf6f48eee44265156c111456a700ab3483686b3f96df4cf5481c89157319" 1234 | dependencies = [ 1235 | "unicode-xid 0.2.0", 1236 | ] 1237 | 1238 | [[package]] 1239 | name = "quick-error" 1240 | version = "1.2.3" 1241 | source = "registry+https://github.com/rust-lang/crates.io-index" 1242 | checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" 1243 | 1244 | [[package]] 1245 | name = "quote" 1246 | version = "0.3.15" 1247 | source = "registry+https://github.com/rust-lang/crates.io-index" 1248 | checksum = "7a6e920b65c65f10b2ae65c831a81a073a89edd28c7cce89475bff467ab4167a" 1249 | 1250 | [[package]] 1251 | name = "quote" 1252 | version = "0.6.13" 1253 | source = "registry+https://github.com/rust-lang/crates.io-index" 1254 | checksum = "6ce23b6b870e8f94f81fb0a363d65d86675884b34a09043c81e5562f11c1f8e1" 1255 | dependencies = [ 1256 | "proc-macro2 0.4.30", 1257 | ] 1258 | 1259 | [[package]] 1260 | name = "quote" 1261 | version = "1.0.4" 1262 | source = "registry+https://github.com/rust-lang/crates.io-index" 1263 | checksum = "4c1f4b0efa5fc5e8ceb705136bfee52cfdb6a4e3509f770b478cd6ed434232a7" 1264 | dependencies = [ 1265 | "proc-macro2 1.0.12", 1266 | ] 1267 | 1268 | [[package]] 1269 | name = "rand" 1270 | version = "0.6.5" 1271 | source = "registry+https://github.com/rust-lang/crates.io-index" 1272 | checksum = "6d71dacdc3c88c1fde3885a3be3fbab9f35724e6ce99467f7d9c5026132184ca" 1273 | dependencies = [ 1274 | "autocfg 0.1.7", 1275 | "libc", 1276 | "rand_chacha 0.1.1", 1277 | "rand_core 0.4.2", 1278 | "rand_hc 0.1.0", 1279 | "rand_isaac", 1280 | "rand_jitter", 1281 | "rand_os", 1282 | "rand_pcg", 1283 | "rand_xorshift", 1284 | "winapi 0.3.8", 1285 | ] 1286 | 1287 | [[package]] 1288 | name = "rand" 1289 | version = "0.7.3" 1290 | source = "registry+https://github.com/rust-lang/crates.io-index" 1291 | checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" 1292 | dependencies = [ 1293 | "getrandom", 1294 | "libc", 1295 | "rand_chacha 0.2.2", 1296 | "rand_core 0.5.1", 1297 | "rand_hc 0.2.0", 1298 | ] 1299 | 1300 | [[package]] 1301 | name = "rand_chacha" 1302 | version = "0.1.1" 1303 | source = "registry+https://github.com/rust-lang/crates.io-index" 1304 | checksum = "556d3a1ca6600bfcbab7c7c91ccb085ac7fbbcd70e008a98742e7847f4f7bcef" 1305 | dependencies = [ 1306 | "autocfg 0.1.7", 1307 | "rand_core 0.3.1", 1308 | ] 1309 | 1310 | [[package]] 1311 | name = "rand_chacha" 1312 | version = "0.2.2" 1313 | source = "registry+https://github.com/rust-lang/crates.io-index" 1314 | checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" 1315 | dependencies = [ 1316 | "ppv-lite86", 1317 | "rand_core 0.5.1", 1318 | ] 1319 | 1320 | [[package]] 1321 | name = "rand_core" 1322 | version = "0.3.1" 1323 | source = "registry+https://github.com/rust-lang/crates.io-index" 1324 | checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" 1325 | dependencies = [ 1326 | "rand_core 0.4.2", 1327 | ] 1328 | 1329 | [[package]] 1330 | name = "rand_core" 1331 | version = "0.4.2" 1332 | source = "registry+https://github.com/rust-lang/crates.io-index" 1333 | checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc" 1334 | 1335 | [[package]] 1336 | name = "rand_core" 1337 | version = "0.5.1" 1338 | source = "registry+https://github.com/rust-lang/crates.io-index" 1339 | checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" 1340 | dependencies = [ 1341 | "getrandom", 1342 | ] 1343 | 1344 | [[package]] 1345 | name = "rand_hc" 1346 | version = "0.1.0" 1347 | source = "registry+https://github.com/rust-lang/crates.io-index" 1348 | checksum = "7b40677c7be09ae76218dc623efbf7b18e34bced3f38883af07bb75630a21bc4" 1349 | dependencies = [ 1350 | "rand_core 0.3.1", 1351 | ] 1352 | 1353 | [[package]] 1354 | name = "rand_hc" 1355 | version = "0.2.0" 1356 | source = "registry+https://github.com/rust-lang/crates.io-index" 1357 | checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" 1358 | dependencies = [ 1359 | "rand_core 0.5.1", 1360 | ] 1361 | 1362 | [[package]] 1363 | name = "rand_isaac" 1364 | version = "0.1.1" 1365 | source = "registry+https://github.com/rust-lang/crates.io-index" 1366 | checksum = "ded997c9d5f13925be2a6fd7e66bf1872597f759fd9dd93513dd7e92e5a5ee08" 1367 | dependencies = [ 1368 | "rand_core 0.3.1", 1369 | ] 1370 | 1371 | [[package]] 1372 | name = "rand_jitter" 1373 | version = "0.1.4" 1374 | source = "registry+https://github.com/rust-lang/crates.io-index" 1375 | checksum = "1166d5c91dc97b88d1decc3285bb0a99ed84b05cfd0bc2341bdf2d43fc41e39b" 1376 | dependencies = [ 1377 | "libc", 1378 | "rand_core 0.4.2", 1379 | "winapi 0.3.8", 1380 | ] 1381 | 1382 | [[package]] 1383 | name = "rand_os" 1384 | version = "0.1.3" 1385 | source = "registry+https://github.com/rust-lang/crates.io-index" 1386 | checksum = "7b75f676a1e053fc562eafbb47838d67c84801e38fc1ba459e8f180deabd5071" 1387 | dependencies = [ 1388 | "cloudabi", 1389 | "fuchsia-cprng", 1390 | "libc", 1391 | "rand_core 0.4.2", 1392 | "rdrand", 1393 | "winapi 0.3.8", 1394 | ] 1395 | 1396 | [[package]] 1397 | name = "rand_pcg" 1398 | version = "0.1.2" 1399 | source = "registry+https://github.com/rust-lang/crates.io-index" 1400 | checksum = "abf9b09b01790cfe0364f52bf32995ea3c39f4d2dd011eac241d2914146d0b44" 1401 | dependencies = [ 1402 | "autocfg 0.1.7", 1403 | "rand_core 0.4.2", 1404 | ] 1405 | 1406 | [[package]] 1407 | name = "rand_xorshift" 1408 | version = "0.1.1" 1409 | source = "registry+https://github.com/rust-lang/crates.io-index" 1410 | checksum = "cbf7e9e623549b0e21f6e97cf8ecf247c1a8fd2e8a992ae265314300b2455d5c" 1411 | dependencies = [ 1412 | "rand_core 0.3.1", 1413 | ] 1414 | 1415 | [[package]] 1416 | name = "rayon" 1417 | version = "1.3.0" 1418 | source = "registry+https://github.com/rust-lang/crates.io-index" 1419 | checksum = "db6ce3297f9c85e16621bb8cca38a06779ffc31bb8184e1be4bed2be4678a098" 1420 | dependencies = [ 1421 | "crossbeam-deque", 1422 | "either", 1423 | "rayon-core", 1424 | ] 1425 | 1426 | [[package]] 1427 | name = "rayon-core" 1428 | version = "1.7.0" 1429 | source = "registry+https://github.com/rust-lang/crates.io-index" 1430 | checksum = "08a89b46efaf957e52b18062fb2f4660f8b8a4dde1807ca002690868ef2c85a9" 1431 | dependencies = [ 1432 | "crossbeam-deque", 1433 | "crossbeam-queue", 1434 | "crossbeam-utils", 1435 | "lazy_static", 1436 | "num_cpus", 1437 | ] 1438 | 1439 | [[package]] 1440 | name = "rdrand" 1441 | version = "0.4.0" 1442 | source = "registry+https://github.com/rust-lang/crates.io-index" 1443 | checksum = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" 1444 | dependencies = [ 1445 | "rand_core 0.3.1", 1446 | ] 1447 | 1448 | [[package]] 1449 | name = "redox_syscall" 1450 | version = "0.1.56" 1451 | source = "registry+https://github.com/rust-lang/crates.io-index" 1452 | checksum = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84" 1453 | 1454 | [[package]] 1455 | name = "regex" 1456 | version = "1.3.7" 1457 | source = "registry+https://github.com/rust-lang/crates.io-index" 1458 | checksum = "a6020f034922e3194c711b82a627453881bc4682166cabb07134a10c26ba7692" 1459 | dependencies = [ 1460 | "aho-corasick", 1461 | "memchr", 1462 | "regex-syntax", 1463 | "thread_local", 1464 | ] 1465 | 1466 | [[package]] 1467 | name = "regex-syntax" 1468 | version = "0.6.17" 1469 | source = "registry+https://github.com/rust-lang/crates.io-index" 1470 | checksum = "7fe5bd57d1d7414c6b5ed48563a2c855d995ff777729dcd91c369ec7fea395ae" 1471 | 1472 | [[package]] 1473 | name = "remove_dir_all" 1474 | version = "0.5.2" 1475 | source = "registry+https://github.com/rust-lang/crates.io-index" 1476 | checksum = "4a83fa3702a688b9359eccba92d153ac33fd2e8462f9e0e3fdf155239ea7792e" 1477 | dependencies = [ 1478 | "winapi 0.3.8", 1479 | ] 1480 | 1481 | [[package]] 1482 | name = "reqwest" 1483 | version = "0.10.4" 1484 | source = "registry+https://github.com/rust-lang/crates.io-index" 1485 | checksum = "02b81e49ddec5109a9dcfc5f2a317ff53377c915e9ae9d4f2fb50914b85614e2" 1486 | dependencies = [ 1487 | "base64", 1488 | "bytes 0.5.4", 1489 | "encoding_rs", 1490 | "futures-core", 1491 | "futures-util", 1492 | "http", 1493 | "http-body", 1494 | "hyper", 1495 | "hyper-rustls", 1496 | "js-sys", 1497 | "lazy_static", 1498 | "log", 1499 | "mime", 1500 | "mime_guess", 1501 | "percent-encoding", 1502 | "pin-project-lite", 1503 | "rustls", 1504 | "serde", 1505 | "serde_json", 1506 | "serde_urlencoded", 1507 | "time", 1508 | "tokio 0.2.20", 1509 | "tokio-rustls", 1510 | "url", 1511 | "wasm-bindgen", 1512 | "wasm-bindgen-futures", 1513 | "web-sys", 1514 | "webpki-roots", 1515 | "winreg", 1516 | ] 1517 | 1518 | [[package]] 1519 | name = "ring" 1520 | version = "0.16.12" 1521 | source = "registry+https://github.com/rust-lang/crates.io-index" 1522 | checksum = "1ba5a8ec64ee89a76c98c549af81ff14813df09c3e6dc4766c3856da48597a0c" 1523 | dependencies = [ 1524 | "cc", 1525 | "lazy_static", 1526 | "libc", 1527 | "spin", 1528 | "untrusted", 1529 | "web-sys", 1530 | "winapi 0.3.8", 1531 | ] 1532 | 1533 | [[package]] 1534 | name = "rustc-demangle" 1535 | version = "0.1.16" 1536 | source = "registry+https://github.com/rust-lang/crates.io-index" 1537 | checksum = "4c691c0e608126e00913e33f0ccf3727d5fc84573623b8d65b2df340b5201783" 1538 | 1539 | [[package]] 1540 | name = "rustc_version" 1541 | version = "0.2.3" 1542 | source = "registry+https://github.com/rust-lang/crates.io-index" 1543 | checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" 1544 | dependencies = [ 1545 | "semver", 1546 | ] 1547 | 1548 | [[package]] 1549 | name = "rustls" 1550 | version = "0.17.0" 1551 | source = "registry+https://github.com/rust-lang/crates.io-index" 1552 | checksum = "c0d4a31f5d68413404705d6982529b0e11a9aacd4839d1d6222ee3b8cb4015e1" 1553 | dependencies = [ 1554 | "base64", 1555 | "log", 1556 | "ring", 1557 | "sct", 1558 | "webpki", 1559 | ] 1560 | 1561 | [[package]] 1562 | name = "rustls-native-certs" 1563 | version = "0.3.0" 1564 | source = "registry+https://github.com/rust-lang/crates.io-index" 1565 | checksum = "a75ffeb84a6bd9d014713119542ce415db3a3e4748f0bfce1e1416cd224a23a5" 1566 | dependencies = [ 1567 | "openssl-probe", 1568 | "rustls", 1569 | "schannel", 1570 | "security-framework", 1571 | ] 1572 | 1573 | [[package]] 1574 | name = "ryu" 1575 | version = "1.0.4" 1576 | source = "registry+https://github.com/rust-lang/crates.io-index" 1577 | checksum = "ed3d612bc64430efeb3f7ee6ef26d590dce0c43249217bddc62112540c7941e1" 1578 | 1579 | [[package]] 1580 | name = "schannel" 1581 | version = "0.1.18" 1582 | source = "registry+https://github.com/rust-lang/crates.io-index" 1583 | checksum = "039c25b130bd8c1321ee2d7de7fde2659fa9c2744e4bb29711cfc852ea53cd19" 1584 | dependencies = [ 1585 | "lazy_static", 1586 | "winapi 0.3.8", 1587 | ] 1588 | 1589 | [[package]] 1590 | name = "scopeguard" 1591 | version = "1.1.0" 1592 | source = "registry+https://github.com/rust-lang/crates.io-index" 1593 | checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" 1594 | 1595 | [[package]] 1596 | name = "sct" 1597 | version = "0.6.0" 1598 | source = "registry+https://github.com/rust-lang/crates.io-index" 1599 | checksum = "e3042af939fca8c3453b7af0f1c66e533a15a86169e39de2657310ade8f98d3c" 1600 | dependencies = [ 1601 | "ring", 1602 | "untrusted", 1603 | ] 1604 | 1605 | [[package]] 1606 | name = "security-framework" 1607 | version = "0.4.4" 1608 | source = "registry+https://github.com/rust-lang/crates.io-index" 1609 | checksum = "64808902d7d99f78eaddd2b4e2509713babc3dc3c85ad6f4c447680f3c01e535" 1610 | dependencies = [ 1611 | "bitflags", 1612 | "core-foundation", 1613 | "core-foundation-sys", 1614 | "libc", 1615 | "security-framework-sys", 1616 | ] 1617 | 1618 | [[package]] 1619 | name = "security-framework-sys" 1620 | version = "0.4.3" 1621 | source = "registry+https://github.com/rust-lang/crates.io-index" 1622 | checksum = "17bf11d99252f512695eb468de5516e5cf75455521e69dfe343f3b74e4748405" 1623 | dependencies = [ 1624 | "core-foundation-sys", 1625 | "libc", 1626 | ] 1627 | 1628 | [[package]] 1629 | name = "semver" 1630 | version = "0.9.0" 1631 | source = "registry+https://github.com/rust-lang/crates.io-index" 1632 | checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" 1633 | dependencies = [ 1634 | "semver-parser", 1635 | ] 1636 | 1637 | [[package]] 1638 | name = "semver-parser" 1639 | version = "0.7.0" 1640 | source = "registry+https://github.com/rust-lang/crates.io-index" 1641 | checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" 1642 | 1643 | [[package]] 1644 | name = "serde" 1645 | version = "1.0.107" 1646 | source = "registry+https://github.com/rust-lang/crates.io-index" 1647 | checksum = "eba7550f2cdf88ffc23ab0f1607133486c390a8c0f89b57e589b9654ee15e04d" 1648 | dependencies = [ 1649 | "serde_derive", 1650 | ] 1651 | 1652 | [[package]] 1653 | name = "serde_bytes" 1654 | version = "0.11.4" 1655 | source = "registry+https://github.com/rust-lang/crates.io-index" 1656 | checksum = "3bf487fbf5c6239d7ea2ff8b10cb6b811cd4b5080d1c2aeed1dec18753c06e10" 1657 | dependencies = [ 1658 | "serde", 1659 | ] 1660 | 1661 | [[package]] 1662 | name = "serde_derive" 1663 | version = "1.0.107" 1664 | source = "registry+https://github.com/rust-lang/crates.io-index" 1665 | checksum = "10be45e22e5597d4b88afcc71f9d7bfadcd604bf0c78a3ab4582b8d2b37f39f3" 1666 | dependencies = [ 1667 | "proc-macro2 1.0.12", 1668 | "quote 1.0.4", 1669 | "syn 1.0.19", 1670 | ] 1671 | 1672 | [[package]] 1673 | name = "serde_json" 1674 | version = "1.0.52" 1675 | source = "registry+https://github.com/rust-lang/crates.io-index" 1676 | checksum = "a7894c8ed05b7a3a279aeb79025fdec1d3158080b75b98a08faf2806bb799edd" 1677 | dependencies = [ 1678 | "itoa", 1679 | "ryu", 1680 | "serde", 1681 | ] 1682 | 1683 | [[package]] 1684 | name = "serde_urlencoded" 1685 | version = "0.6.1" 1686 | source = "registry+https://github.com/rust-lang/crates.io-index" 1687 | checksum = "9ec5d77e2d4c73717816afac02670d5c4f534ea95ed430442cad02e7a6e32c97" 1688 | dependencies = [ 1689 | "dtoa", 1690 | "itoa", 1691 | "serde", 1692 | "url", 1693 | ] 1694 | 1695 | [[package]] 1696 | name = "sha2" 1697 | version = "0.7.1" 1698 | source = "registry+https://github.com/rust-lang/crates.io-index" 1699 | checksum = "9eb6be24e4c23a84d7184280d2722f7f2731fcdd4a9d886efbfe4413e4847ea0" 1700 | dependencies = [ 1701 | "block-buffer 0.3.3", 1702 | "byte-tools 0.2.0", 1703 | "digest 0.7.6", 1704 | "fake-simd", 1705 | ] 1706 | 1707 | [[package]] 1708 | name = "sha2" 1709 | version = "0.8.1" 1710 | source = "registry+https://github.com/rust-lang/crates.io-index" 1711 | checksum = "27044adfd2e1f077f649f59deb9490d3941d674002f7d062870a60ebe9bd47a0" 1712 | dependencies = [ 1713 | "block-buffer 0.7.3", 1714 | "digest 0.8.1", 1715 | "fake-simd", 1716 | "opaque-debug", 1717 | ] 1718 | 1719 | [[package]] 1720 | name = "slab" 1721 | version = "0.4.2" 1722 | source = "registry+https://github.com/rust-lang/crates.io-index" 1723 | checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" 1724 | 1725 | [[package]] 1726 | name = "smallvec" 1727 | version = "0.6.13" 1728 | source = "registry+https://github.com/rust-lang/crates.io-index" 1729 | checksum = "f7b0758c52e15a8b5e3691eae6cc559f08eee9406e548a4477ba4e67770a82b6" 1730 | dependencies = [ 1731 | "maybe-uninit", 1732 | ] 1733 | 1734 | [[package]] 1735 | name = "smallvec" 1736 | version = "1.4.0" 1737 | source = "registry+https://github.com/rust-lang/crates.io-index" 1738 | checksum = "c7cb5678e1615754284ec264d9bb5b4c27d2018577fd90ac0ceb578591ed5ee4" 1739 | 1740 | [[package]] 1741 | name = "solana-bpf-loader-program" 1742 | version = "1.0.9" 1743 | source = "registry+https://github.com/rust-lang/crates.io-index" 1744 | checksum = "f4a666163d6feb624ed5bccc01fcb8255cdcd0755443cf0e66e70bdc398514ac" 1745 | dependencies = [ 1746 | "bincode", 1747 | "byteorder", 1748 | "libc", 1749 | "log", 1750 | "num-derive 0.3.0", 1751 | "num-traits 0.2.11", 1752 | "solana-logger", 1753 | "solana-sdk", 1754 | "solana_rbpf", 1755 | "thiserror", 1756 | ] 1757 | 1758 | [[package]] 1759 | name = "solana-bpf-token" 1760 | version = "0.1.0" 1761 | dependencies = [ 1762 | "num-derive 0.2.5", 1763 | "num-traits 0.2.11", 1764 | "solana-sdk", 1765 | "solana-sdk-bpf-test", 1766 | "thiserror", 1767 | ] 1768 | 1769 | [[package]] 1770 | name = "solana-bpf-token-tests" 1771 | version = "0.1.0" 1772 | dependencies = [ 1773 | "byteorder", 1774 | "num-derive 0.2.5", 1775 | "num-traits 0.2.11", 1776 | "solana-bpf-loader-program", 1777 | "solana-bpf-token", 1778 | "solana-logger", 1779 | "solana-sdk", 1780 | "solana-sdk-bpf-test", 1781 | ] 1782 | 1783 | [[package]] 1784 | name = "solana-crate-features" 1785 | version = "1.1.7" 1786 | source = "registry+https://github.com/rust-lang/crates.io-index" 1787 | checksum = "d8b9e89b0ad8df5063b9000ed75f839693d3f02baca6ca5136400ab18025bd23" 1788 | dependencies = [ 1789 | "backtrace", 1790 | "bytes 0.4.12", 1791 | "cc", 1792 | "curve25519-dalek", 1793 | "ed25519-dalek", 1794 | "either", 1795 | "failure", 1796 | "lazy_static", 1797 | "libc", 1798 | "rand_chacha 0.1.1", 1799 | "regex-syntax", 1800 | "reqwest", 1801 | "serde", 1802 | "syn 0.15.44", 1803 | "syn 1.0.19", 1804 | "tokio 0.1.22", 1805 | "winapi 0.3.8", 1806 | ] 1807 | 1808 | [[package]] 1809 | name = "solana-logger" 1810 | version = "1.0.9" 1811 | source = "registry+https://github.com/rust-lang/crates.io-index" 1812 | checksum = "f5aaac8a6323fc230cac2fad5fb44ffba1a9f2e0c38b3bba36f5c9c4e547d371" 1813 | dependencies = [ 1814 | "env_logger 0.7.1", 1815 | "lazy_static", 1816 | "log", 1817 | ] 1818 | 1819 | [[package]] 1820 | name = "solana-sdk" 1821 | version = "1.0.9" 1822 | source = "registry+https://github.com/rust-lang/crates.io-index" 1823 | checksum = "33ff4aad064cf78a348293f2e03e5861a01784e860a2f2e1b7cc5673afb54fa6" 1824 | dependencies = [ 1825 | "assert_matches", 1826 | "bincode", 1827 | "bs58", 1828 | "bv", 1829 | "byteorder", 1830 | "chrono", 1831 | "ed25519-dalek", 1832 | "generic-array 0.13.2", 1833 | "hex", 1834 | "hmac", 1835 | "itertools 0.8.2", 1836 | "log", 1837 | "memmap", 1838 | "num-derive 0.3.0", 1839 | "num-traits 0.2.11", 1840 | "pbkdf2", 1841 | "rand 0.6.5", 1842 | "rand_chacha 0.1.1", 1843 | "serde", 1844 | "serde_bytes", 1845 | "serde_derive", 1846 | "serde_json", 1847 | "sha2 0.8.1", 1848 | "solana-crate-features", 1849 | "solana-logger", 1850 | "solana-sdk-macro", 1851 | "thiserror", 1852 | ] 1853 | 1854 | [[package]] 1855 | name = "solana-sdk-bpf-test" 1856 | version = "1.1.8" 1857 | 1858 | [[package]] 1859 | name = "solana-sdk-macro" 1860 | version = "1.1.10" 1861 | source = "registry+https://github.com/rust-lang/crates.io-index" 1862 | checksum = "f16eea85cbeaae139fa6ca20f8e05022c4a87c271e64ccd5bf296f2692305e6e" 1863 | dependencies = [ 1864 | "bs58", 1865 | "proc-macro2 1.0.12", 1866 | "quote 1.0.4", 1867 | "syn 1.0.19", 1868 | ] 1869 | 1870 | [[package]] 1871 | name = "solana_rbpf" 1872 | version = "0.1.21" 1873 | source = "registry+https://github.com/rust-lang/crates.io-index" 1874 | checksum = "053e2a0f1f6c6298bf832493aeacdd6df98efb756a90feabd33caca9c708f2be" 1875 | dependencies = [ 1876 | "byteorder", 1877 | "combine", 1878 | "elfkit", 1879 | "hash32", 1880 | "libc", 1881 | "log", 1882 | "num-traits 0.2.11", 1883 | "time", 1884 | ] 1885 | 1886 | [[package]] 1887 | name = "spin" 1888 | version = "0.5.2" 1889 | source = "registry+https://github.com/rust-lang/crates.io-index" 1890 | checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" 1891 | 1892 | [[package]] 1893 | name = "strsim" 1894 | version = "0.8.0" 1895 | source = "registry+https://github.com/rust-lang/crates.io-index" 1896 | checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" 1897 | 1898 | [[package]] 1899 | name = "subtle" 1900 | version = "1.0.0" 1901 | source = "registry+https://github.com/rust-lang/crates.io-index" 1902 | checksum = "2d67a5a62ba6e01cb2192ff309324cb4875d0c451d55fe2319433abe7a05a8ee" 1903 | 1904 | [[package]] 1905 | name = "subtle" 1906 | version = "2.2.2" 1907 | source = "registry+https://github.com/rust-lang/crates.io-index" 1908 | checksum = "7c65d530b10ccaeac294f349038a597e435b18fb456aadd0840a623f83b9e941" 1909 | 1910 | [[package]] 1911 | name = "syn" 1912 | version = "0.11.11" 1913 | source = "registry+https://github.com/rust-lang/crates.io-index" 1914 | checksum = "d3b891b9015c88c576343b9b3e41c2c11a51c219ef067b264bd9c8aa9b441dad" 1915 | dependencies = [ 1916 | "quote 0.3.15", 1917 | "synom", 1918 | "unicode-xid 0.0.4", 1919 | ] 1920 | 1921 | [[package]] 1922 | name = "syn" 1923 | version = "0.15.44" 1924 | source = "registry+https://github.com/rust-lang/crates.io-index" 1925 | checksum = "9ca4b3b69a77cbe1ffc9e198781b7acb0c7365a883670e8f1c1bc66fba79a5c5" 1926 | dependencies = [ 1927 | "proc-macro2 0.4.30", 1928 | "quote 0.6.13", 1929 | "unicode-xid 0.1.0", 1930 | ] 1931 | 1932 | [[package]] 1933 | name = "syn" 1934 | version = "1.0.19" 1935 | source = "registry+https://github.com/rust-lang/crates.io-index" 1936 | checksum = "e8e5aa70697bb26ee62214ae3288465ecec0000f05182f039b477001f08f5ae7" 1937 | dependencies = [ 1938 | "proc-macro2 1.0.12", 1939 | "quote 1.0.4", 1940 | "unicode-xid 0.2.0", 1941 | ] 1942 | 1943 | [[package]] 1944 | name = "synom" 1945 | version = "0.11.3" 1946 | source = "registry+https://github.com/rust-lang/crates.io-index" 1947 | checksum = "a393066ed9010ebaed60b9eafa373d4b1baac186dd7e008555b0f702b51945b6" 1948 | dependencies = [ 1949 | "unicode-xid 0.0.4", 1950 | ] 1951 | 1952 | [[package]] 1953 | name = "synstructure" 1954 | version = "0.12.3" 1955 | source = "registry+https://github.com/rust-lang/crates.io-index" 1956 | checksum = "67656ea1dc1b41b1451851562ea232ec2e5a80242139f7e679ceccfb5d61f545" 1957 | dependencies = [ 1958 | "proc-macro2 1.0.12", 1959 | "quote 1.0.4", 1960 | "syn 1.0.19", 1961 | "unicode-xid 0.2.0", 1962 | ] 1963 | 1964 | [[package]] 1965 | name = "tempfile" 1966 | version = "3.1.0" 1967 | source = "registry+https://github.com/rust-lang/crates.io-index" 1968 | checksum = "7a6e24d9338a0a5be79593e2fa15a648add6138caa803e2d5bc782c371732ca9" 1969 | dependencies = [ 1970 | "cfg-if", 1971 | "libc", 1972 | "rand 0.7.3", 1973 | "redox_syscall", 1974 | "remove_dir_all", 1975 | "winapi 0.3.8", 1976 | ] 1977 | 1978 | [[package]] 1979 | name = "termcolor" 1980 | version = "1.1.0" 1981 | source = "registry+https://github.com/rust-lang/crates.io-index" 1982 | checksum = "bb6bfa289a4d7c5766392812c0a1f4c1ba45afa1ad47803c11e1f407d846d75f" 1983 | dependencies = [ 1984 | "winapi-util", 1985 | ] 1986 | 1987 | [[package]] 1988 | name = "textwrap" 1989 | version = "0.11.0" 1990 | source = "registry+https://github.com/rust-lang/crates.io-index" 1991 | checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" 1992 | dependencies = [ 1993 | "unicode-width", 1994 | ] 1995 | 1996 | [[package]] 1997 | name = "thiserror" 1998 | version = "1.0.16" 1999 | source = "registry+https://github.com/rust-lang/crates.io-index" 2000 | checksum = "d12a1dae4add0f0d568eebc7bf142f145ba1aa2544cafb195c76f0f409091b60" 2001 | dependencies = [ 2002 | "thiserror-impl", 2003 | ] 2004 | 2005 | [[package]] 2006 | name = "thiserror-impl" 2007 | version = "1.0.16" 2008 | source = "registry+https://github.com/rust-lang/crates.io-index" 2009 | checksum = "3f34e0c1caaa462fd840ec6b768946ea1e7842620d94fe29d5b847138f521269" 2010 | dependencies = [ 2011 | "proc-macro2 1.0.12", 2012 | "quote 1.0.4", 2013 | "syn 1.0.19", 2014 | ] 2015 | 2016 | [[package]] 2017 | name = "thread_local" 2018 | version = "1.0.1" 2019 | source = "registry+https://github.com/rust-lang/crates.io-index" 2020 | checksum = "d40c6d1b69745a6ec6fb1ca717914848da4b44ae29d9b3080cbee91d72a69b14" 2021 | dependencies = [ 2022 | "lazy_static", 2023 | ] 2024 | 2025 | [[package]] 2026 | name = "time" 2027 | version = "0.1.43" 2028 | source = "registry+https://github.com/rust-lang/crates.io-index" 2029 | checksum = "ca8a50ef2360fbd1eeb0ecd46795a87a19024eb4b53c5dc916ca1fd95fe62438" 2030 | dependencies = [ 2031 | "libc", 2032 | "winapi 0.3.8", 2033 | ] 2034 | 2035 | [[package]] 2036 | name = "tokio" 2037 | version = "0.1.22" 2038 | source = "registry+https://github.com/rust-lang/crates.io-index" 2039 | checksum = "5a09c0b5bb588872ab2f09afa13ee6e9dac11e10a0ec9e8e3ba39a5a5d530af6" 2040 | dependencies = [ 2041 | "bytes 0.4.12", 2042 | "futures", 2043 | "mio", 2044 | "num_cpus", 2045 | "tokio-codec", 2046 | "tokio-current-thread", 2047 | "tokio-executor", 2048 | "tokio-fs", 2049 | "tokio-io", 2050 | "tokio-reactor", 2051 | "tokio-sync", 2052 | "tokio-tcp", 2053 | "tokio-threadpool", 2054 | "tokio-timer", 2055 | "tokio-udp", 2056 | "tokio-uds", 2057 | ] 2058 | 2059 | [[package]] 2060 | name = "tokio" 2061 | version = "0.2.20" 2062 | source = "registry+https://github.com/rust-lang/crates.io-index" 2063 | checksum = "05c1d570eb1a36f0345a5ce9c6c6e665b70b73d11236912c0b477616aeec47b1" 2064 | dependencies = [ 2065 | "bytes 0.5.4", 2066 | "fnv", 2067 | "futures-core", 2068 | "iovec", 2069 | "lazy_static", 2070 | "memchr", 2071 | "mio", 2072 | "num_cpus", 2073 | "pin-project-lite", 2074 | "slab", 2075 | ] 2076 | 2077 | [[package]] 2078 | name = "tokio-codec" 2079 | version = "0.1.2" 2080 | source = "registry+https://github.com/rust-lang/crates.io-index" 2081 | checksum = "25b2998660ba0e70d18684de5d06b70b70a3a747469af9dea7618cc59e75976b" 2082 | dependencies = [ 2083 | "bytes 0.4.12", 2084 | "futures", 2085 | "tokio-io", 2086 | ] 2087 | 2088 | [[package]] 2089 | name = "tokio-current-thread" 2090 | version = "0.1.7" 2091 | source = "registry+https://github.com/rust-lang/crates.io-index" 2092 | checksum = "b1de0e32a83f131e002238d7ccde18211c0a5397f60cbfffcb112868c2e0e20e" 2093 | dependencies = [ 2094 | "futures", 2095 | "tokio-executor", 2096 | ] 2097 | 2098 | [[package]] 2099 | name = "tokio-executor" 2100 | version = "0.1.10" 2101 | source = "registry+https://github.com/rust-lang/crates.io-index" 2102 | checksum = "fb2d1b8f4548dbf5e1f7818512e9c406860678f29c300cdf0ebac72d1a3a1671" 2103 | dependencies = [ 2104 | "crossbeam-utils", 2105 | "futures", 2106 | ] 2107 | 2108 | [[package]] 2109 | name = "tokio-fs" 2110 | version = "0.1.7" 2111 | source = "registry+https://github.com/rust-lang/crates.io-index" 2112 | checksum = "297a1206e0ca6302a0eed35b700d292b275256f596e2f3fea7729d5e629b6ff4" 2113 | dependencies = [ 2114 | "futures", 2115 | "tokio-io", 2116 | "tokio-threadpool", 2117 | ] 2118 | 2119 | [[package]] 2120 | name = "tokio-io" 2121 | version = "0.1.13" 2122 | source = "registry+https://github.com/rust-lang/crates.io-index" 2123 | checksum = "57fc868aae093479e3131e3d165c93b1c7474109d13c90ec0dda2a1bbfff0674" 2124 | dependencies = [ 2125 | "bytes 0.4.12", 2126 | "futures", 2127 | "log", 2128 | ] 2129 | 2130 | [[package]] 2131 | name = "tokio-reactor" 2132 | version = "0.1.12" 2133 | source = "registry+https://github.com/rust-lang/crates.io-index" 2134 | checksum = "09bc590ec4ba8ba87652da2068d150dcada2cfa2e07faae270a5e0409aa51351" 2135 | dependencies = [ 2136 | "crossbeam-utils", 2137 | "futures", 2138 | "lazy_static", 2139 | "log", 2140 | "mio", 2141 | "num_cpus", 2142 | "parking_lot", 2143 | "slab", 2144 | "tokio-executor", 2145 | "tokio-io", 2146 | "tokio-sync", 2147 | ] 2148 | 2149 | [[package]] 2150 | name = "tokio-rustls" 2151 | version = "0.13.0" 2152 | source = "registry+https://github.com/rust-lang/crates.io-index" 2153 | checksum = "4adb8b3e5f86b707f1b54e7c15b6de52617a823608ccda98a15d3a24222f265a" 2154 | dependencies = [ 2155 | "futures-core", 2156 | "rustls", 2157 | "tokio 0.2.20", 2158 | "webpki", 2159 | ] 2160 | 2161 | [[package]] 2162 | name = "tokio-sync" 2163 | version = "0.1.8" 2164 | source = "registry+https://github.com/rust-lang/crates.io-index" 2165 | checksum = "edfe50152bc8164fcc456dab7891fa9bf8beaf01c5ee7e1dd43a397c3cf87dee" 2166 | dependencies = [ 2167 | "fnv", 2168 | "futures", 2169 | ] 2170 | 2171 | [[package]] 2172 | name = "tokio-tcp" 2173 | version = "0.1.4" 2174 | source = "registry+https://github.com/rust-lang/crates.io-index" 2175 | checksum = "98df18ed66e3b72e742f185882a9e201892407957e45fbff8da17ae7a7c51f72" 2176 | dependencies = [ 2177 | "bytes 0.4.12", 2178 | "futures", 2179 | "iovec", 2180 | "mio", 2181 | "tokio-io", 2182 | "tokio-reactor", 2183 | ] 2184 | 2185 | [[package]] 2186 | name = "tokio-threadpool" 2187 | version = "0.1.18" 2188 | source = "registry+https://github.com/rust-lang/crates.io-index" 2189 | checksum = "df720b6581784c118f0eb4310796b12b1d242a7eb95f716a8367855325c25f89" 2190 | dependencies = [ 2191 | "crossbeam-deque", 2192 | "crossbeam-queue", 2193 | "crossbeam-utils", 2194 | "futures", 2195 | "lazy_static", 2196 | "log", 2197 | "num_cpus", 2198 | "slab", 2199 | "tokio-executor", 2200 | ] 2201 | 2202 | [[package]] 2203 | name = "tokio-timer" 2204 | version = "0.2.13" 2205 | source = "registry+https://github.com/rust-lang/crates.io-index" 2206 | checksum = "93044f2d313c95ff1cb7809ce9a7a05735b012288a888b62d4434fd58c94f296" 2207 | dependencies = [ 2208 | "crossbeam-utils", 2209 | "futures", 2210 | "slab", 2211 | "tokio-executor", 2212 | ] 2213 | 2214 | [[package]] 2215 | name = "tokio-udp" 2216 | version = "0.1.6" 2217 | source = "registry+https://github.com/rust-lang/crates.io-index" 2218 | checksum = "e2a0b10e610b39c38b031a2fcab08e4b82f16ece36504988dcbd81dbba650d82" 2219 | dependencies = [ 2220 | "bytes 0.4.12", 2221 | "futures", 2222 | "log", 2223 | "mio", 2224 | "tokio-codec", 2225 | "tokio-io", 2226 | "tokio-reactor", 2227 | ] 2228 | 2229 | [[package]] 2230 | name = "tokio-uds" 2231 | version = "0.2.6" 2232 | source = "registry+https://github.com/rust-lang/crates.io-index" 2233 | checksum = "5076db410d6fdc6523df7595447629099a1fdc47b3d9f896220780fa48faf798" 2234 | dependencies = [ 2235 | "bytes 0.4.12", 2236 | "futures", 2237 | "iovec", 2238 | "libc", 2239 | "log", 2240 | "mio", 2241 | "mio-uds", 2242 | "tokio-codec", 2243 | "tokio-io", 2244 | "tokio-reactor", 2245 | ] 2246 | 2247 | [[package]] 2248 | name = "tokio-util" 2249 | version = "0.3.1" 2250 | source = "registry+https://github.com/rust-lang/crates.io-index" 2251 | checksum = "be8242891f2b6cbef26a2d7e8605133c2c554cd35b3e4948ea892d6d68436499" 2252 | dependencies = [ 2253 | "bytes 0.5.4", 2254 | "futures-core", 2255 | "futures-sink", 2256 | "log", 2257 | "pin-project-lite", 2258 | "tokio 0.2.20", 2259 | ] 2260 | 2261 | [[package]] 2262 | name = "tower-service" 2263 | version = "0.3.0" 2264 | source = "registry+https://github.com/rust-lang/crates.io-index" 2265 | checksum = "e987b6bf443f4b5b3b6f38704195592cca41c5bb7aedd3c3693c7081f8289860" 2266 | 2267 | [[package]] 2268 | name = "try-lock" 2269 | version = "0.2.2" 2270 | source = "registry+https://github.com/rust-lang/crates.io-index" 2271 | checksum = "e604eb7b43c06650e854be16a2a03155743d3752dd1c943f6829e26b7a36e382" 2272 | 2273 | [[package]] 2274 | name = "typenum" 2275 | version = "1.12.0" 2276 | source = "registry+https://github.com/rust-lang/crates.io-index" 2277 | checksum = "373c8a200f9e67a0c95e62a4f52fbf80c23b4381c05a17845531982fa99e6b33" 2278 | 2279 | [[package]] 2280 | name = "unicase" 2281 | version = "2.6.0" 2282 | source = "registry+https://github.com/rust-lang/crates.io-index" 2283 | checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6" 2284 | dependencies = [ 2285 | "version_check", 2286 | ] 2287 | 2288 | [[package]] 2289 | name = "unicode-bidi" 2290 | version = "0.3.4" 2291 | source = "registry+https://github.com/rust-lang/crates.io-index" 2292 | checksum = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5" 2293 | dependencies = [ 2294 | "matches", 2295 | ] 2296 | 2297 | [[package]] 2298 | name = "unicode-normalization" 2299 | version = "0.1.12" 2300 | source = "registry+https://github.com/rust-lang/crates.io-index" 2301 | checksum = "5479532badd04e128284890390c1e876ef7a993d0570b3597ae43dfa1d59afa4" 2302 | dependencies = [ 2303 | "smallvec 1.4.0", 2304 | ] 2305 | 2306 | [[package]] 2307 | name = "unicode-width" 2308 | version = "0.1.7" 2309 | source = "registry+https://github.com/rust-lang/crates.io-index" 2310 | checksum = "caaa9d531767d1ff2150b9332433f32a24622147e5ebb1f26409d5da67afd479" 2311 | 2312 | [[package]] 2313 | name = "unicode-xid" 2314 | version = "0.0.4" 2315 | source = "registry+https://github.com/rust-lang/crates.io-index" 2316 | checksum = "8c1f860d7d29cf02cb2f3f359fd35991af3d30bac52c57d265a3c461074cb4dc" 2317 | 2318 | [[package]] 2319 | name = "unicode-xid" 2320 | version = "0.1.0" 2321 | source = "registry+https://github.com/rust-lang/crates.io-index" 2322 | checksum = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" 2323 | 2324 | [[package]] 2325 | name = "unicode-xid" 2326 | version = "0.2.0" 2327 | source = "registry+https://github.com/rust-lang/crates.io-index" 2328 | checksum = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" 2329 | 2330 | [[package]] 2331 | name = "untrusted" 2332 | version = "0.7.1" 2333 | source = "registry+https://github.com/rust-lang/crates.io-index" 2334 | checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" 2335 | 2336 | [[package]] 2337 | name = "url" 2338 | version = "2.1.1" 2339 | source = "registry+https://github.com/rust-lang/crates.io-index" 2340 | checksum = "829d4a8476c35c9bf0bbce5a3b23f4106f79728039b726d292bb93bc106787cb" 2341 | dependencies = [ 2342 | "idna", 2343 | "matches", 2344 | "percent-encoding", 2345 | ] 2346 | 2347 | [[package]] 2348 | name = "vec_map" 2349 | version = "0.8.2" 2350 | source = "registry+https://github.com/rust-lang/crates.io-index" 2351 | checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" 2352 | 2353 | [[package]] 2354 | name = "version_check" 2355 | version = "0.9.1" 2356 | source = "registry+https://github.com/rust-lang/crates.io-index" 2357 | checksum = "078775d0255232fb988e6fccf26ddc9d1ac274299aaedcedce21c6f72cc533ce" 2358 | 2359 | [[package]] 2360 | name = "want" 2361 | version = "0.3.0" 2362 | source = "registry+https://github.com/rust-lang/crates.io-index" 2363 | checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" 2364 | dependencies = [ 2365 | "log", 2366 | "try-lock", 2367 | ] 2368 | 2369 | [[package]] 2370 | name = "wasi" 2371 | version = "0.9.0+wasi-snapshot-preview1" 2372 | source = "registry+https://github.com/rust-lang/crates.io-index" 2373 | checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" 2374 | 2375 | [[package]] 2376 | name = "wasm-bindgen" 2377 | version = "0.2.62" 2378 | source = "registry+https://github.com/rust-lang/crates.io-index" 2379 | checksum = "e3c7d40d09cdbf0f4895ae58cf57d92e1e57a9dd8ed2e8390514b54a47cc5551" 2380 | dependencies = [ 2381 | "cfg-if", 2382 | "serde", 2383 | "serde_json", 2384 | "wasm-bindgen-macro", 2385 | ] 2386 | 2387 | [[package]] 2388 | name = "wasm-bindgen-backend" 2389 | version = "0.2.62" 2390 | source = "registry+https://github.com/rust-lang/crates.io-index" 2391 | checksum = "c3972e137ebf830900db522d6c8fd74d1900dcfc733462e9a12e942b00b4ac94" 2392 | dependencies = [ 2393 | "bumpalo", 2394 | "lazy_static", 2395 | "log", 2396 | "proc-macro2 1.0.12", 2397 | "quote 1.0.4", 2398 | "syn 1.0.19", 2399 | "wasm-bindgen-shared", 2400 | ] 2401 | 2402 | [[package]] 2403 | name = "wasm-bindgen-futures" 2404 | version = "0.4.12" 2405 | source = "registry+https://github.com/rust-lang/crates.io-index" 2406 | checksum = "8a369c5e1dfb7569e14d62af4da642a3cbc2f9a3652fe586e26ac22222aa4b04" 2407 | dependencies = [ 2408 | "cfg-if", 2409 | "js-sys", 2410 | "wasm-bindgen", 2411 | "web-sys", 2412 | ] 2413 | 2414 | [[package]] 2415 | name = "wasm-bindgen-macro" 2416 | version = "0.2.62" 2417 | source = "registry+https://github.com/rust-lang/crates.io-index" 2418 | checksum = "2cd85aa2c579e8892442954685f0d801f9129de24fa2136b2c6a539c76b65776" 2419 | dependencies = [ 2420 | "quote 1.0.4", 2421 | "wasm-bindgen-macro-support", 2422 | ] 2423 | 2424 | [[package]] 2425 | name = "wasm-bindgen-macro-support" 2426 | version = "0.2.62" 2427 | source = "registry+https://github.com/rust-lang/crates.io-index" 2428 | checksum = "8eb197bd3a47553334907ffd2f16507b4f4f01bbec3ac921a7719e0decdfe72a" 2429 | dependencies = [ 2430 | "proc-macro2 1.0.12", 2431 | "quote 1.0.4", 2432 | "syn 1.0.19", 2433 | "wasm-bindgen-backend", 2434 | "wasm-bindgen-shared", 2435 | ] 2436 | 2437 | [[package]] 2438 | name = "wasm-bindgen-shared" 2439 | version = "0.2.62" 2440 | source = "registry+https://github.com/rust-lang/crates.io-index" 2441 | checksum = "a91c2916119c17a8e316507afaaa2dd94b47646048014bbdf6bef098c1bb58ad" 2442 | 2443 | [[package]] 2444 | name = "web-sys" 2445 | version = "0.3.39" 2446 | source = "registry+https://github.com/rust-lang/crates.io-index" 2447 | checksum = "8bc359e5dd3b46cb9687a051d50a2fdd228e4ba7cf6fcf861a5365c3d671a642" 2448 | dependencies = [ 2449 | "js-sys", 2450 | "wasm-bindgen", 2451 | ] 2452 | 2453 | [[package]] 2454 | name = "webpki" 2455 | version = "0.21.2" 2456 | source = "registry+https://github.com/rust-lang/crates.io-index" 2457 | checksum = "f1f50e1972865d6b1adb54167d1c8ed48606004c2c9d0ea5f1eeb34d95e863ef" 2458 | dependencies = [ 2459 | "ring", 2460 | "untrusted", 2461 | ] 2462 | 2463 | [[package]] 2464 | name = "webpki-roots" 2465 | version = "0.18.0" 2466 | source = "registry+https://github.com/rust-lang/crates.io-index" 2467 | checksum = "91cd5736df7f12a964a5067a12c62fa38e1bd8080aff1f80bc29be7c80d19ab4" 2468 | dependencies = [ 2469 | "webpki", 2470 | ] 2471 | 2472 | [[package]] 2473 | name = "winapi" 2474 | version = "0.2.8" 2475 | source = "registry+https://github.com/rust-lang/crates.io-index" 2476 | checksum = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" 2477 | 2478 | [[package]] 2479 | name = "winapi" 2480 | version = "0.3.8" 2481 | source = "registry+https://github.com/rust-lang/crates.io-index" 2482 | checksum = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6" 2483 | dependencies = [ 2484 | "winapi-i686-pc-windows-gnu", 2485 | "winapi-x86_64-pc-windows-gnu", 2486 | ] 2487 | 2488 | [[package]] 2489 | name = "winapi-build" 2490 | version = "0.1.1" 2491 | source = "registry+https://github.com/rust-lang/crates.io-index" 2492 | checksum = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" 2493 | 2494 | [[package]] 2495 | name = "winapi-i686-pc-windows-gnu" 2496 | version = "0.4.0" 2497 | source = "registry+https://github.com/rust-lang/crates.io-index" 2498 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 2499 | 2500 | [[package]] 2501 | name = "winapi-util" 2502 | version = "0.1.5" 2503 | source = "registry+https://github.com/rust-lang/crates.io-index" 2504 | checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" 2505 | dependencies = [ 2506 | "winapi 0.3.8", 2507 | ] 2508 | 2509 | [[package]] 2510 | name = "winapi-x86_64-pc-windows-gnu" 2511 | version = "0.4.0" 2512 | source = "registry+https://github.com/rust-lang/crates.io-index" 2513 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 2514 | 2515 | [[package]] 2516 | name = "winreg" 2517 | version = "0.6.2" 2518 | source = "registry+https://github.com/rust-lang/crates.io-index" 2519 | checksum = "b2986deb581c4fe11b621998a5e53361efe6b48a151178d0cd9eeffa4dc6acc9" 2520 | dependencies = [ 2521 | "winapi 0.3.8", 2522 | ] 2523 | 2524 | [[package]] 2525 | name = "ws2_32-sys" 2526 | version = "0.2.1" 2527 | source = "registry+https://github.com/rust-lang/crates.io-index" 2528 | checksum = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e" 2529 | dependencies = [ 2530 | "winapi 0.2.8", 2531 | "winapi-build", 2532 | ] 2533 | -------------------------------------------------------------------------------- /src/program-test/Cargo.toml: -------------------------------------------------------------------------------- 1 | 2 | # Note: This crate must be built using do.sh 3 | 4 | [package] 5 | name = "solana-bpf-token-tests" 6 | version = "0.1.0" 7 | description = "Example token program written in Rust" 8 | authors = ["Solana Maintainers "] 9 | repository = "https://github.com/solana-labs/solana" 10 | license = "Apache-2.0" 11 | homepage = "https://solana.com/" 12 | edition = "2018" 13 | 14 | [dependencies] 15 | byteorder = "1.3.2" 16 | num-derive = "0.2" 17 | num-traits = "0.2" 18 | solana-bpf-token = { path = "../program" } 19 | solana-sdk = { version = "=1.0.9", default-features = false, features=["program"] } 20 | solana-sdk-bpf-test = { path = "../../node_modules/@solana/web3.js/bpf-sdk/rust/test", default-features = false } 21 | solana-bpf-loader-program = { version = "=1.0.9" } 22 | solana-logger = { version = "=1.0.9" } 23 | 24 | -------------------------------------------------------------------------------- /src/program-test/tests/bench.rs: -------------------------------------------------------------------------------- 1 | use byteorder::{LittleEndian, WriteBytesExt}; 2 | use solana_bpf_loader_program::{create_vm, deserialize_parameters, serialize_parameters}; 3 | use solana_bpf_token::state::{Command, State, Token, TokenAccount}; 4 | use solana_sdk::{ 5 | account::{Account, KeyedAccount}, 6 | instruction::InstructionError, 7 | pubkey::Pubkey, 8 | }; 9 | use std::{cell::RefCell, fs::File, io::Read, mem::size_of, path::PathBuf}; 10 | 11 | #[test] 12 | pub fn serde() { 13 | assert_eq!(State::deserialize(&[0]), Ok(State::default())); 14 | 15 | let mut data = vec![0; 256]; 16 | 17 | let account = State::Account(TokenAccount { 18 | token: Pubkey::new(&[1; 32]), 19 | owner: Pubkey::new(&[2; 32]), 20 | amount: 123, 21 | delegate: None, 22 | }); 23 | account.serialize(&mut data).unwrap(); 24 | assert_eq!(State::deserialize(&data), Ok(account)); 25 | 26 | let account = State::Token(Token { 27 | supply: 12345, 28 | decimals: 2, 29 | }); 30 | account.serialize(&mut data).unwrap(); 31 | assert_eq!(State::deserialize(&data), Ok(account)); 32 | } 33 | 34 | fn load_program(name: &str) -> Vec { 35 | let mut path = PathBuf::new(); 36 | path.push("../program/target/bpfel-unknown-unknown/release"); 37 | path.push(name); 38 | path.set_extension("so"); 39 | 40 | let mut file = File::open(path).unwrap(); 41 | let mut program = Vec::new(); 42 | file.read_to_end(&mut program).unwrap(); 43 | program 44 | } 45 | 46 | fn run_program( 47 | program_id: &Pubkey, 48 | parameter_accounts: &[KeyedAccount], 49 | instruction_data: &[u8], 50 | ) -> Result<(u64, u64), InstructionError> { 51 | let mut program_account = Account::default(); 52 | program_account.data = load_program("solana_bpf_token"); 53 | let (mut vm, heap_region) = create_vm(&program_account.data).unwrap(); 54 | 55 | let mut parameter_bytes = 56 | serialize_parameters(program_id, parameter_accounts, &instruction_data).unwrap(); 57 | let result = vm.execute_program(parameter_bytes.as_mut_slice(), &[], &[heap_region.clone()]); 58 | let result = result.unwrap(); 59 | deserialize_parameters(parameter_accounts, ¶meter_bytes).unwrap(); 60 | let instruction_count = vm.get_last_instruction_count(); 61 | Ok((result, instruction_count)) 62 | } 63 | 64 | /// Not really random, based on line!() 65 | macro_rules! pubkey_new_rand { 66 | () => {{ 67 | let mut v = Vec::new(); 68 | for _ in 0..32 / std::mem::size_of::() { 69 | v.write_u32::(line!()).unwrap(); 70 | } 71 | Pubkey::new(&v[..]) 72 | }}; 73 | } 74 | 75 | #[test] 76 | fn bench() { 77 | solana_logger::setup(); 78 | 79 | let program_id = pubkey_new_rand!(); 80 | let mut instruction_data = vec![0u8; size_of::()]; 81 | let mint_key = pubkey_new_rand!(); 82 | let mut mint_account = Account::new_ref(0, size_of::(), &program_id); 83 | let owner_key = pubkey_new_rand!(); 84 | let mut owner_account = RefCell::new(Account::default()); 85 | let token_key = pubkey_new_rand!(); 86 | let mut token_account = Account::new_ref(0, size_of::(), &program_id); 87 | 88 | // Create mint account 89 | let instruction = Command::NewTokenAccount; 90 | instruction.serialize(&mut instruction_data).unwrap(); 91 | let parameter_accounts = vec![ 92 | KeyedAccount::new(&mint_key, true, &mut mint_account), 93 | KeyedAccount::new(&owner_key, false, &mut owner_account), 94 | KeyedAccount::new(&token_key, false, &mut token_account), 95 | ]; 96 | let (result, newtokenaccount_count) = 97 | run_program(&program_id, ¶meter_accounts[..], &instruction_data).unwrap(); 98 | assert!(result == 0); 99 | 100 | // Create new account 101 | let instruction = Command::NewTokenAccount; 102 | instruction.serialize(&mut instruction_data).unwrap(); 103 | let payee_key = pubkey_new_rand!(); 104 | let mut payee_account = Account::new_ref(0, size_of::(), &program_id); 105 | let parameter_accounts = vec![ 106 | KeyedAccount::new(&payee_key, true, &mut payee_account), 107 | KeyedAccount::new(&owner_key, false, &mut owner_account), 108 | KeyedAccount::new(&token_key, false, &mut token_account), 109 | ]; 110 | let (result, _) = run_program(&program_id, ¶meter_accounts[..], &instruction_data).unwrap(); 111 | assert!(result == 0); 112 | 113 | // Create new token 114 | let instruction = Command::NewToken(Token { 115 | supply: 1000, 116 | decimals: 2, 117 | }); 118 | instruction.serialize(&mut instruction_data).unwrap(); 119 | let parameter_accounts = vec![ 120 | KeyedAccount::new(&token_key, true, &mut token_account), 121 | KeyedAccount::new(&mint_key, false, &mut mint_account), 122 | ]; 123 | let (result, newtoken_count) = 124 | run_program(&program_id, ¶meter_accounts[..], &instruction_data).unwrap(); 125 | assert!(result == 0); 126 | 127 | // Transfer 128 | let instruction = Command::Transfer(100); 129 | instruction.serialize(&mut instruction_data).unwrap(); 130 | let parameter_accounts = vec![ 131 | KeyedAccount::new(&owner_key, true, &mut owner_account), 132 | KeyedAccount::new(&mint_key, false, &mut mint_account), 133 | KeyedAccount::new(&payee_key, false, &mut payee_account), 134 | ]; 135 | let (result, transfer_count) = 136 | run_program(&program_id, ¶meter_accounts[..], &instruction_data).unwrap(); 137 | assert!(result == 0); 138 | 139 | const BASELINE_NEWTOKENACCOUNT_COUNT: u64 = 1500; // last known 1317 140 | const BASELINE_NEWTOKEN_COUNT: u64 = 1500; // last known 1340 141 | const BASELINE_TRANSFER_COUNT: u64 = 2500; // last known 2161 142 | 143 | println!("BPF instructions executed"); 144 | println!( 145 | " NewTokenAccount: {:?} ({:?})", 146 | newtokenaccount_count, BASELINE_NEWTOKENACCOUNT_COUNT 147 | ); 148 | println!( 149 | " NewToken : {:?} ({:?})", 150 | newtoken_count, BASELINE_NEWTOKEN_COUNT 151 | ); 152 | println!( 153 | " Transfer : {:?} ({:?})", 154 | transfer_count, BASELINE_TRANSFER_COUNT 155 | ); 156 | 157 | assert!(newtokenaccount_count <= BASELINE_NEWTOKENACCOUNT_COUNT); 158 | assert!(newtoken_count <= BASELINE_NEWTOKEN_COUNT); 159 | assert!(transfer_count <= BASELINE_TRANSFER_COUNT); 160 | } 161 | -------------------------------------------------------------------------------- /src/program/.gitignore: -------------------------------------------------------------------------------- 1 | /target/ 2 | -------------------------------------------------------------------------------- /src/program/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | [[package]] 4 | name = "autocfg" 5 | version = "1.0.0" 6 | source = "registry+https://github.com/rust-lang/crates.io-index" 7 | checksum = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d" 8 | 9 | [[package]] 10 | name = "bincode" 11 | version = "1.2.1" 12 | source = "registry+https://github.com/rust-lang/crates.io-index" 13 | checksum = "5753e2a71534719bf3f4e57006c3a4f0d2c672a4b676eec84161f763eca87dbf" 14 | dependencies = [ 15 | "byteorder", 16 | "serde", 17 | ] 18 | 19 | [[package]] 20 | name = "block-buffer" 21 | version = "0.7.3" 22 | source = "registry+https://github.com/rust-lang/crates.io-index" 23 | checksum = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b" 24 | dependencies = [ 25 | "block-padding", 26 | "byte-tools", 27 | "byteorder", 28 | "generic-array 0.12.3", 29 | ] 30 | 31 | [[package]] 32 | name = "block-padding" 33 | version = "0.1.5" 34 | source = "registry+https://github.com/rust-lang/crates.io-index" 35 | checksum = "fa79dedbb091f449f1f39e53edf88d5dbe95f895dae6135a8d7b881fb5af73f5" 36 | dependencies = [ 37 | "byte-tools", 38 | ] 39 | 40 | [[package]] 41 | name = "bs58" 42 | version = "0.3.1" 43 | source = "registry+https://github.com/rust-lang/crates.io-index" 44 | checksum = "476e9cd489f9e121e02ffa6014a8ef220ecb15c05ed23fc34cca13925dc283fb" 45 | 46 | [[package]] 47 | name = "bv" 48 | version = "0.11.1" 49 | source = "registry+https://github.com/rust-lang/crates.io-index" 50 | checksum = "8834bb1d8ee5dc048ee3124f2c7c1afcc6bc9aed03f11e9dfd8c69470a5db340" 51 | dependencies = [ 52 | "feature-probe", 53 | "serde", 54 | ] 55 | 56 | [[package]] 57 | name = "byte-tools" 58 | version = "0.3.1" 59 | source = "registry+https://github.com/rust-lang/crates.io-index" 60 | checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" 61 | 62 | [[package]] 63 | name = "byteorder" 64 | version = "1.3.4" 65 | source = "registry+https://github.com/rust-lang/crates.io-index" 66 | checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de" 67 | 68 | [[package]] 69 | name = "cfg-if" 70 | version = "0.1.10" 71 | source = "registry+https://github.com/rust-lang/crates.io-index" 72 | checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" 73 | 74 | [[package]] 75 | name = "crypto-mac" 76 | version = "0.7.0" 77 | source = "registry+https://github.com/rust-lang/crates.io-index" 78 | checksum = "4434400df11d95d556bac068ddfedd482915eb18fe8bea89bc80b6e4b1c179e5" 79 | dependencies = [ 80 | "generic-array 0.12.3", 81 | "subtle", 82 | ] 83 | 84 | [[package]] 85 | name = "digest" 86 | version = "0.8.1" 87 | source = "registry+https://github.com/rust-lang/crates.io-index" 88 | checksum = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5" 89 | dependencies = [ 90 | "generic-array 0.12.3", 91 | ] 92 | 93 | [[package]] 94 | name = "either" 95 | version = "1.5.3" 96 | source = "registry+https://github.com/rust-lang/crates.io-index" 97 | checksum = "bb1f6b1ce1c140482ea30ddd3335fc0024ac7ee112895426e0a629a6c20adfe3" 98 | 99 | [[package]] 100 | name = "fake-simd" 101 | version = "0.1.2" 102 | source = "registry+https://github.com/rust-lang/crates.io-index" 103 | checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" 104 | 105 | [[package]] 106 | name = "feature-probe" 107 | version = "0.1.1" 108 | source = "registry+https://github.com/rust-lang/crates.io-index" 109 | checksum = "835a3dc7d1ec9e75e2b5fb4ba75396837112d2060b03f7d43bc1897c7f7211da" 110 | 111 | [[package]] 112 | name = "generic-array" 113 | version = "0.12.3" 114 | source = "registry+https://github.com/rust-lang/crates.io-index" 115 | checksum = "c68f0274ae0e023facc3c97b2e00f076be70e254bc851d972503b328db79b2ec" 116 | dependencies = [ 117 | "typenum", 118 | ] 119 | 120 | [[package]] 121 | name = "generic-array" 122 | version = "0.13.2" 123 | source = "registry+https://github.com/rust-lang/crates.io-index" 124 | checksum = "0ed1e761351b56f54eb9dcd0cfaca9fd0daecf93918e1cfc01c8a3d26ee7adcd" 125 | dependencies = [ 126 | "serde", 127 | "typenum", 128 | ] 129 | 130 | [[package]] 131 | name = "hex" 132 | version = "0.4.2" 133 | source = "registry+https://github.com/rust-lang/crates.io-index" 134 | checksum = "644f9158b2f133fd50f5fb3242878846d9eb792e445c893805ff0e3824006e35" 135 | 136 | [[package]] 137 | name = "hmac" 138 | version = "0.7.1" 139 | source = "registry+https://github.com/rust-lang/crates.io-index" 140 | checksum = "5dcb5e64cda4c23119ab41ba960d1e170a774c8e4b9d9e6a9bc18aabf5e59695" 141 | dependencies = [ 142 | "crypto-mac", 143 | "digest", 144 | ] 145 | 146 | [[package]] 147 | name = "itertools" 148 | version = "0.8.2" 149 | source = "registry+https://github.com/rust-lang/crates.io-index" 150 | checksum = "f56a2d0bc861f9165be4eb3442afd3c236d8a98afd426f65d92324ae1091a484" 151 | dependencies = [ 152 | "either", 153 | ] 154 | 155 | [[package]] 156 | name = "log" 157 | version = "0.4.8" 158 | source = "registry+https://github.com/rust-lang/crates.io-index" 159 | checksum = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7" 160 | dependencies = [ 161 | "cfg-if", 162 | ] 163 | 164 | [[package]] 165 | name = "num-derive" 166 | version = "0.2.5" 167 | source = "registry+https://github.com/rust-lang/crates.io-index" 168 | checksum = "eafd0b45c5537c3ba526f79d3e75120036502bebacbb3f3220914067ce39dbf2" 169 | dependencies = [ 170 | "proc-macro2 0.4.30", 171 | "quote 0.6.13", 172 | "syn 0.15.44", 173 | ] 174 | 175 | [[package]] 176 | name = "num-derive" 177 | version = "0.3.0" 178 | source = "registry+https://github.com/rust-lang/crates.io-index" 179 | checksum = "0c8b15b261814f992e33760b1fca9fe8b693d8a65299f20c9901688636cfb746" 180 | dependencies = [ 181 | "proc-macro2 1.0.12", 182 | "quote 1.0.4", 183 | "syn 1.0.19", 184 | ] 185 | 186 | [[package]] 187 | name = "num-traits" 188 | version = "0.2.11" 189 | source = "registry+https://github.com/rust-lang/crates.io-index" 190 | checksum = "c62be47e61d1842b9170f0fdeec8eba98e60e90e5446449a0545e5152acd7096" 191 | dependencies = [ 192 | "autocfg", 193 | ] 194 | 195 | [[package]] 196 | name = "opaque-debug" 197 | version = "0.2.3" 198 | source = "registry+https://github.com/rust-lang/crates.io-index" 199 | checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" 200 | 201 | [[package]] 202 | name = "pbkdf2" 203 | version = "0.3.0" 204 | source = "registry+https://github.com/rust-lang/crates.io-index" 205 | checksum = "006c038a43a45995a9670da19e67600114740e8511d4333bf97a56e66a7542d9" 206 | dependencies = [ 207 | "byteorder", 208 | "crypto-mac", 209 | ] 210 | 211 | [[package]] 212 | name = "proc-macro2" 213 | version = "0.4.30" 214 | source = "registry+https://github.com/rust-lang/crates.io-index" 215 | checksum = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759" 216 | dependencies = [ 217 | "unicode-xid 0.1.0", 218 | ] 219 | 220 | [[package]] 221 | name = "proc-macro2" 222 | version = "1.0.12" 223 | source = "registry+https://github.com/rust-lang/crates.io-index" 224 | checksum = "8872cf6f48eee44265156c111456a700ab3483686b3f96df4cf5481c89157319" 225 | dependencies = [ 226 | "unicode-xid 0.2.0", 227 | ] 228 | 229 | [[package]] 230 | name = "quote" 231 | version = "0.6.13" 232 | source = "registry+https://github.com/rust-lang/crates.io-index" 233 | checksum = "6ce23b6b870e8f94f81fb0a363d65d86675884b34a09043c81e5562f11c1f8e1" 234 | dependencies = [ 235 | "proc-macro2 0.4.30", 236 | ] 237 | 238 | [[package]] 239 | name = "quote" 240 | version = "1.0.4" 241 | source = "registry+https://github.com/rust-lang/crates.io-index" 242 | checksum = "4c1f4b0efa5fc5e8ceb705136bfee52cfdb6a4e3509f770b478cd6ed434232a7" 243 | dependencies = [ 244 | "proc-macro2 1.0.12", 245 | ] 246 | 247 | [[package]] 248 | name = "serde" 249 | version = "1.0.107" 250 | source = "registry+https://github.com/rust-lang/crates.io-index" 251 | checksum = "eba7550f2cdf88ffc23ab0f1607133486c390a8c0f89b57e589b9654ee15e04d" 252 | dependencies = [ 253 | "serde_derive", 254 | ] 255 | 256 | [[package]] 257 | name = "serde_bytes" 258 | version = "0.11.4" 259 | source = "registry+https://github.com/rust-lang/crates.io-index" 260 | checksum = "3bf487fbf5c6239d7ea2ff8b10cb6b811cd4b5080d1c2aeed1dec18753c06e10" 261 | dependencies = [ 262 | "serde", 263 | ] 264 | 265 | [[package]] 266 | name = "serde_derive" 267 | version = "1.0.107" 268 | source = "registry+https://github.com/rust-lang/crates.io-index" 269 | checksum = "10be45e22e5597d4b88afcc71f9d7bfadcd604bf0c78a3ab4582b8d2b37f39f3" 270 | dependencies = [ 271 | "proc-macro2 1.0.12", 272 | "quote 1.0.4", 273 | "syn 1.0.19", 274 | ] 275 | 276 | [[package]] 277 | name = "sha2" 278 | version = "0.8.1" 279 | source = "registry+https://github.com/rust-lang/crates.io-index" 280 | checksum = "27044adfd2e1f077f649f59deb9490d3941d674002f7d062870a60ebe9bd47a0" 281 | dependencies = [ 282 | "block-buffer", 283 | "digest", 284 | "fake-simd", 285 | "opaque-debug", 286 | ] 287 | 288 | [[package]] 289 | name = "solana-bpf-token" 290 | version = "0.1.0" 291 | dependencies = [ 292 | "num-derive 0.2.5", 293 | "num-traits", 294 | "solana-sdk", 295 | "solana-sdk-bpf-test", 296 | "thiserror", 297 | ] 298 | 299 | [[package]] 300 | name = "solana-sdk" 301 | version = "1.0.9" 302 | source = "registry+https://github.com/rust-lang/crates.io-index" 303 | checksum = "33ff4aad064cf78a348293f2e03e5861a01784e860a2f2e1b7cc5673afb54fa6" 304 | dependencies = [ 305 | "bincode", 306 | "bs58", 307 | "bv", 308 | "generic-array 0.13.2", 309 | "hex", 310 | "hmac", 311 | "itertools", 312 | "log", 313 | "num-derive 0.3.0", 314 | "num-traits", 315 | "pbkdf2", 316 | "serde", 317 | "serde_bytes", 318 | "serde_derive", 319 | "sha2", 320 | "solana-sdk-macro", 321 | "thiserror", 322 | ] 323 | 324 | [[package]] 325 | name = "solana-sdk-bpf-test" 326 | version = "1.1.8" 327 | 328 | [[package]] 329 | name = "solana-sdk-macro" 330 | version = "1.1.10" 331 | source = "registry+https://github.com/rust-lang/crates.io-index" 332 | checksum = "f16eea85cbeaae139fa6ca20f8e05022c4a87c271e64ccd5bf296f2692305e6e" 333 | dependencies = [ 334 | "bs58", 335 | "proc-macro2 1.0.12", 336 | "quote 1.0.4", 337 | "syn 1.0.19", 338 | ] 339 | 340 | [[package]] 341 | name = "subtle" 342 | version = "1.0.0" 343 | source = "registry+https://github.com/rust-lang/crates.io-index" 344 | checksum = "2d67a5a62ba6e01cb2192ff309324cb4875d0c451d55fe2319433abe7a05a8ee" 345 | 346 | [[package]] 347 | name = "syn" 348 | version = "0.15.44" 349 | source = "registry+https://github.com/rust-lang/crates.io-index" 350 | checksum = "9ca4b3b69a77cbe1ffc9e198781b7acb0c7365a883670e8f1c1bc66fba79a5c5" 351 | dependencies = [ 352 | "proc-macro2 0.4.30", 353 | "quote 0.6.13", 354 | "unicode-xid 0.1.0", 355 | ] 356 | 357 | [[package]] 358 | name = "syn" 359 | version = "1.0.19" 360 | source = "registry+https://github.com/rust-lang/crates.io-index" 361 | checksum = "e8e5aa70697bb26ee62214ae3288465ecec0000f05182f039b477001f08f5ae7" 362 | dependencies = [ 363 | "proc-macro2 1.0.12", 364 | "quote 1.0.4", 365 | "unicode-xid 0.2.0", 366 | ] 367 | 368 | [[package]] 369 | name = "thiserror" 370 | version = "1.0.16" 371 | source = "registry+https://github.com/rust-lang/crates.io-index" 372 | checksum = "d12a1dae4add0f0d568eebc7bf142f145ba1aa2544cafb195c76f0f409091b60" 373 | dependencies = [ 374 | "thiserror-impl", 375 | ] 376 | 377 | [[package]] 378 | name = "thiserror-impl" 379 | version = "1.0.16" 380 | source = "registry+https://github.com/rust-lang/crates.io-index" 381 | checksum = "3f34e0c1caaa462fd840ec6b768946ea1e7842620d94fe29d5b847138f521269" 382 | dependencies = [ 383 | "proc-macro2 1.0.12", 384 | "quote 1.0.4", 385 | "syn 1.0.19", 386 | ] 387 | 388 | [[package]] 389 | name = "typenum" 390 | version = "1.12.0" 391 | source = "registry+https://github.com/rust-lang/crates.io-index" 392 | checksum = "373c8a200f9e67a0c95e62a4f52fbf80c23b4381c05a17845531982fa99e6b33" 393 | 394 | [[package]] 395 | name = "unicode-xid" 396 | version = "0.1.0" 397 | source = "registry+https://github.com/rust-lang/crates.io-index" 398 | checksum = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" 399 | 400 | [[package]] 401 | name = "unicode-xid" 402 | version = "0.2.0" 403 | source = "registry+https://github.com/rust-lang/crates.io-index" 404 | checksum = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" 405 | -------------------------------------------------------------------------------- /src/program/Cargo.toml: -------------------------------------------------------------------------------- 1 | 2 | # Note: This crate must be built using do.sh 3 | 4 | [package] 5 | name = "solana-bpf-token" 6 | version = "0.1.0" 7 | description = "Example token program written in Rust" 8 | authors = ["Solana Maintainers "] 9 | repository = "https://github.com/solana-labs/solana" 10 | license = "Apache-2.0" 11 | homepage = "https://solana.com/" 12 | edition = "2018" 13 | 14 | [dependencies] 15 | num-derive = "0.2" 16 | num-traits = "0.2" 17 | solana-sdk = { version = "=1.0.9", default-features = false, features=["program"] } 18 | solana-sdk-bpf-test = { path = "../../node_modules/@solana/web3.js/bpf-sdk/rust/test", default-features = false } 19 | thiserror = "1.0" 20 | 21 | [lib] 22 | name = "solana_bpf_token" 23 | crate-type = ["cdylib", "lib"] 24 | -------------------------------------------------------------------------------- /src/program/README.md: -------------------------------------------------------------------------------- 1 | 2 | ### Building 3 | 4 | This project cannot be built directly via cargo and instead requires the build scripts located in Solana's BPF-SDK. 5 | 6 | To build via NPM, from the repo's root directory: 7 | 8 | `$ npm run build:program` 9 | 10 | You can also refer to the `build:program` script in `package.json` as an example of how to call the build scripts directly 11 | 12 | ### Testing 13 | 14 | Unit tests contained within this project can be built via: 15 | 16 | `$ ./do.sh test` 17 | 18 | For additional system tests refer to [program-test](`../program-test`). System tests are separated into a different project due to conflicts with dependency features 19 | 20 | ### Clippy 21 | 22 | Clippy is also supported via: 23 | 24 | `$ ./do.sh clippy` 25 | -------------------------------------------------------------------------------- /src/program/Xargo.toml: -------------------------------------------------------------------------------- 1 | [target.bpfel-unknown-unknown.dependencies.std] 2 | features = [] -------------------------------------------------------------------------------- /src/program/do.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | cd "$(dirname "$0")" 4 | 5 | usage() { 6 | cat <"${dump}-mangled.txt" 71 | greadelf \ 72 | -aW \ 73 | "$so" \ 74 | >>"${dump}-mangled.txt" 75 | "$sdkDir/dependencies/llvm-native/bin/llvm-objdump" \ 76 | -print-imm-hex \ 77 | --source \ 78 | --disassemble \ 79 | "$so" \ 80 | >>"${dump}-mangled.txt" 81 | sed \ 82 | s/://g \ 83 | < "${dump}-mangled.txt" \ 84 | | rustfilt \ 85 | > "${dump}.txt" 86 | else 87 | echo "Warning: No dump created, cannot find: $so" 88 | fi 89 | ) 90 | ;; 91 | help) 92 | usage 93 | exit 94 | ;; 95 | *) 96 | echo "Error: Unknown command" 97 | usage 98 | exit 99 | ;; 100 | esac 101 | } 102 | 103 | set -e 104 | 105 | perform_action "$@" 106 | -------------------------------------------------------------------------------- /src/program/src/error.rs: -------------------------------------------------------------------------------- 1 | use num_derive::FromPrimitive; 2 | use num_traits::FromPrimitive; 3 | use solana_sdk::{ 4 | info, 5 | program_error::{PrintProgramError, ProgramError}, 6 | program_utils::DecodeError, 7 | }; 8 | use thiserror::Error; 9 | 10 | #[derive(Clone, Debug, Eq, Error, FromPrimitive, PartialEq)] 11 | pub enum TokenError { 12 | #[error("insufficient funds")] 13 | InsufficientFunds, 14 | #[error("token mismatch")] 15 | TokenMismatch, 16 | #[error("not a delegate")] 17 | NotDelegate, 18 | #[error("no owner")] 19 | NoOwner, 20 | } 21 | 22 | impl From for ProgramError { 23 | fn from(e: TokenError) -> Self { 24 | ProgramError::CustomError(e as u32) 25 | } 26 | } 27 | 28 | impl DecodeError for TokenError { 29 | fn type_of() -> &'static str { 30 | "TokenError" 31 | } 32 | } 33 | 34 | impl PrintProgramError for TokenError { 35 | fn print(&self) 36 | where 37 | E: 'static + std::error::Error + DecodeError + PrintProgramError + FromPrimitive, 38 | { 39 | match self { 40 | TokenError::InsufficientFunds => info!("Error: insufficient funds"), 41 | TokenError::TokenMismatch => info!("Error: token mismatch"), 42 | TokenError::NotDelegate => info!("Error: not a delegate"), 43 | TokenError::NoOwner => info!("Error: no owner"), 44 | } 45 | } 46 | } 47 | 48 | #[cfg(test)] 49 | mod test { 50 | use super::*; 51 | 52 | fn return_token_error_as_program_error() -> ProgramError { 53 | TokenError::TokenMismatch.into() 54 | } 55 | 56 | #[test] 57 | fn test_print_error() { 58 | let error = return_token_error_as_program_error(); 59 | error.print::(); 60 | } 61 | 62 | #[test] 63 | #[should_panic(expected = "CustomError(1)")] 64 | fn test_error_unwrap() { 65 | Err::<(), ProgramError>(return_token_error_as_program_error()).unwrap(); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/program/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod error; 2 | pub mod processor; 3 | pub mod state; 4 | -------------------------------------------------------------------------------- /src/program/src/processor.rs: -------------------------------------------------------------------------------- 1 | use crate::{error::TokenError, state::State}; 2 | use solana_sdk::{ 3 | account_info::AccountInfo, entrypoint, entrypoint::ProgramResult, 4 | program_error::PrintProgramError, pubkey::Pubkey, 5 | }; 6 | 7 | entrypoint!(process_instruction); 8 | fn process_instruction<'a>( 9 | program_id: &Pubkey, 10 | accounts: &'a [AccountInfo<'a>], 11 | instruction_data: &[u8], 12 | ) -> ProgramResult { 13 | if let Err(error) = State::process(program_id, accounts, instruction_data) { 14 | // catch the error so we can print it 15 | error.print::(); 16 | return Err(error); 17 | } 18 | Ok(()) 19 | } 20 | -------------------------------------------------------------------------------- /src/program/src/state.rs: -------------------------------------------------------------------------------- 1 | use crate::error::TokenError; 2 | use solana_sdk::{ 3 | account_info::AccountInfo, entrypoint::ProgramResult, info, program_error::ProgramError, 4 | program_utils::next_account_info, pubkey::Pubkey, 5 | }; 6 | use std::mem::size_of; 7 | 8 | /// Represents a unique token type that all like token accounts must be 9 | /// associated with 10 | #[repr(C)] 11 | #[derive(Clone, Copy, Debug, Default, PartialEq)] 12 | pub struct Token { 13 | /// Total supply of tokens 14 | pub supply: u64, 15 | /// Number of base 10 digits to the right of the decimal place in the total supply 16 | pub decimals: u64, 17 | } 18 | 19 | /// Delegation details 20 | #[repr(C)] 21 | #[derive(Clone, Copy, Debug, Default, PartialEq)] 22 | pub struct TokenAccountDelegate { 23 | /// The source account for the tokens 24 | pub source: Pubkey, 25 | /// The original amount that this delegate account was authorized to spend up to 26 | pub original_amount: u64, 27 | } 28 | 29 | /// Account that holds or may delegate tokens 30 | #[repr(C)] 31 | #[derive(Clone, Copy, Debug, Default, PartialEq)] 32 | pub struct TokenAccount { 33 | /// The kind of token this account holds 34 | pub token: Pubkey, 35 | /// Owner of this account 36 | pub owner: Pubkey, 37 | /// Amount of tokens this account holds 38 | pub amount: u64, 39 | /// If `delegate` None, `amount` belongs to this account. 40 | /// If `delegate` is Option<_>, `amount` represents the remaining allowance 41 | /// of tokens that may be transferred from the `source` account. 42 | pub delegate: Option, 43 | } 44 | 45 | /// Possible states to accounts owned by the token program 46 | #[repr(C)] 47 | #[derive(Clone, Debug, PartialEq)] 48 | pub enum State { 49 | /// Unallocated 50 | Unallocated, 51 | /// Specifies a type of token 52 | Token(Token), 53 | /// Token account 54 | Account(TokenAccount), 55 | /// Invalid state 56 | Invalid, 57 | } 58 | impl Default for State { 59 | fn default() -> Self { 60 | Self::Unallocated 61 | } 62 | } 63 | 64 | /// Commands supported by the token program 65 | #[repr(C)] 66 | #[derive(Clone, Debug, PartialEq)] 67 | pub enum Command { 68 | /// key 0 - New token 69 | /// key 1 - Token account to hold tokens 70 | NewToken(Token), 71 | /// key 0 - New token account 72 | /// key 1 - Owner of the account 73 | /// key 2 - Token this account is associated with 74 | /// key 3 - Source account that this account is a delegate for (optional) 75 | NewTokenAccount, 76 | /// key 0 - Owner of the source account 77 | /// key 1 - Source/Delegate token account 78 | /// key 2 - Destination account 79 | /// key 3 - Source account if key 1 is a delegate (optional) 80 | Transfer(u64), 81 | /// key 0 - Owner of the source account 82 | /// key 1 - Source token account 83 | /// key 3 - Delegate account 84 | Approve(u64), 85 | /// key 0 - Owner of the destination account 86 | /// key 1 - destination token account 87 | /// key 2 - Owner to assign to destination account 88 | SetOwner, 89 | } 90 | 91 | impl<'a> State { 92 | pub fn process_newtoken>>( 93 | account_info_iter: &mut I, 94 | token: Token, 95 | ) -> ProgramResult { 96 | let token_account_info = next_account_info(account_info_iter)?; 97 | let dest_account_info = next_account_info(account_info_iter)?; 98 | 99 | let mut dest_account_data = dest_account_info.data.borrow_mut(); 100 | if let State::Account(mut dest_token_account) = State::deserialize(&dest_account_data)? { 101 | if !token_account_info.is_signer { 102 | info!("Error: token account not a signer"); 103 | return Err(ProgramError::MissingRequiredSignature); 104 | } 105 | if token_account_info.key != &dest_token_account.token { 106 | info!("Error: token mismatch"); 107 | return Err(TokenError::TokenMismatch.into()); 108 | } 109 | if dest_token_account.delegate.is_some() { 110 | info!("Error: Destination account is a delegate and cannot accept tokens"); 111 | return Err(ProgramError::InvalidArgument); 112 | } 113 | 114 | dest_token_account.amount = token.supply; 115 | State::Account(dest_token_account).serialize(&mut dest_account_data)?; 116 | } else { 117 | info!("Error: Destination account is not an Account"); 118 | return Err(ProgramError::InvalidArgument); 119 | } 120 | 121 | if State::Unallocated != State::deserialize(&token_account_info.data.borrow())? { 122 | info!("Error: token account is already allocated"); 123 | return Err(ProgramError::InvalidArgument); 124 | } 125 | 126 | State::Token(token).serialize(&mut token_account_info.data.borrow_mut()) 127 | } 128 | 129 | pub fn process_newaccount>>( 130 | account_info_iter: &mut I, 131 | ) -> ProgramResult { 132 | let new_account_info = next_account_info(account_info_iter)?; 133 | let owner_account_info = next_account_info(account_info_iter)?; 134 | let token_account_info = next_account_info(account_info_iter)?; 135 | 136 | if !new_account_info.is_signer { 137 | info!("Error: new account not a signer"); 138 | return Err(ProgramError::MissingRequiredSignature); 139 | } 140 | 141 | let mut new_account_data = new_account_info.data.borrow_mut(); 142 | 143 | if State::Unallocated != State::deserialize(&new_account_data)? { 144 | info!("Error: account is already allocated"); 145 | return Err(ProgramError::InvalidArgument); 146 | } 147 | 148 | let mut token_account = TokenAccount { 149 | token: *token_account_info.key, 150 | owner: *owner_account_info.key, 151 | amount: 0, 152 | delegate: None, 153 | }; 154 | if let Ok(delegate_account) = next_account_info(account_info_iter) { 155 | token_account.delegate = Some(TokenAccountDelegate { 156 | source: *delegate_account.key, 157 | original_amount: 0, 158 | }); 159 | } 160 | 161 | State::Account(token_account).serialize(&mut new_account_data) 162 | } 163 | 164 | pub fn process_transfer>>( 165 | account_info_iter: &mut I, 166 | amount: u64, 167 | ) -> ProgramResult { 168 | let owner_account_info = next_account_info(account_info_iter)?; 169 | let source_account_info = next_account_info(account_info_iter)?; 170 | let dest_account_info = next_account_info(account_info_iter)?; 171 | 172 | let mut source_data = source_account_info.data.borrow_mut(); 173 | let mut dest_data = dest_account_info.data.borrow_mut(); 174 | if let (State::Account(mut source_account), State::Account(mut dest_account)) = ( 175 | State::deserialize(&source_data)?, 176 | State::deserialize(&dest_data)?, 177 | ) { 178 | if source_account.token != dest_account.token { 179 | info!("Error: token mismatch"); 180 | return Err(TokenError::TokenMismatch.into()); 181 | } 182 | if dest_account.delegate.is_some() { 183 | info!("Error: destination account is a delegate and cannot accept tokens"); 184 | return Err(ProgramError::InvalidArgument); 185 | } 186 | if owner_account_info.key != &source_account.owner { 187 | info!("Error: source account owner not present"); 188 | return Err(TokenError::NoOwner.into()); 189 | } 190 | if !owner_account_info.is_signer { 191 | info!("Error: owner account not a signer"); 192 | return Err(ProgramError::MissingRequiredSignature); 193 | } 194 | if source_account.amount < amount { 195 | return Err(TokenError::InsufficientFunds.into()); 196 | } 197 | 198 | if let Some(ref delegate) = source_account.delegate { 199 | let source_account_info = next_account_info(account_info_iter)?; 200 | let mut actual_source_data = source_account_info.data.borrow_mut(); 201 | if let State::Account(mut actual_source_account) = 202 | State::deserialize(&actual_source_data)? 203 | { 204 | if source_account_info.key != &delegate.source { 205 | info!("Error: Source account is not a delegate payee"); 206 | return Err(TokenError::NotDelegate.into()); 207 | } 208 | 209 | if actual_source_account.amount < amount { 210 | return Err(TokenError::InsufficientFunds.into()); 211 | } 212 | 213 | actual_source_account.amount -= amount; 214 | State::Account(actual_source_account).serialize(&mut actual_source_data)?; 215 | } else { 216 | info!("Error: payee is an invalid account"); 217 | return Err(ProgramError::InvalidArgument); 218 | } 219 | } 220 | 221 | source_account.amount -= amount; 222 | State::Account(source_account).serialize(&mut source_data)?; 223 | 224 | dest_account.amount += amount; 225 | State::Account(dest_account).serialize(&mut dest_data)?; 226 | } else { 227 | info!("Error: destination and/or source accounts are invalid"); 228 | return Err(ProgramError::InvalidArgument); 229 | } 230 | Ok(()) 231 | } 232 | 233 | pub fn process_approve>>( 234 | account_info_iter: &mut I, 235 | amount: u64, 236 | ) -> ProgramResult { 237 | let owner_account_info = next_account_info(account_info_iter)?; 238 | let source_account_info = next_account_info(account_info_iter)?; 239 | let delegate_account_info = next_account_info(account_info_iter)?; 240 | 241 | let source_data = source_account_info.data.borrow_mut(); 242 | let mut delegate_data = delegate_account_info.data.borrow_mut(); 243 | if let (State::Account(source_account), State::Account(mut delegate_account)) = ( 244 | State::deserialize(&source_data)?, 245 | State::deserialize(&delegate_data)?, 246 | ) { 247 | if source_account.token != delegate_account.token { 248 | info!("Error: token mismatch"); 249 | return Err(TokenError::TokenMismatch.into()); 250 | } 251 | if owner_account_info.key != &source_account.owner { 252 | info!("Error: source account owner is not present"); 253 | return Err(TokenError::NoOwner.into()); 254 | } 255 | if !owner_account_info.is_signer { 256 | info!("Error: owner account not a signer"); 257 | return Err(ProgramError::MissingRequiredSignature); 258 | } 259 | if source_account.delegate.is_some() { 260 | info!("Error: source account is a delegate"); 261 | return Err(ProgramError::InvalidArgument); 262 | } 263 | 264 | match &delegate_account.delegate { 265 | None => { 266 | info!("Error: delegate account is not a delegate"); 267 | return Err(TokenError::NotDelegate.into()); 268 | } 269 | Some(delegate) => { 270 | if source_account_info.key != &delegate.source { 271 | info!("Error: delegate account is not a delegate of the source account"); 272 | return Err(TokenError::NotDelegate.into()); 273 | } 274 | 275 | delegate_account.amount = amount; 276 | delegate_account.delegate = Some(TokenAccountDelegate { 277 | source: delegate.source, 278 | original_amount: amount, 279 | }); 280 | State::Account(delegate_account).serialize(&mut delegate_data)?; 281 | } 282 | } 283 | } else { 284 | info!("Error: destination and/or source accounts are not Accounts"); 285 | return Err(ProgramError::InvalidArgument); 286 | } 287 | Ok(()) 288 | } 289 | 290 | pub fn process_setowner>>( 291 | account_info_iter: &mut I, 292 | ) -> ProgramResult { 293 | let owner_account_info = next_account_info(account_info_iter)?; 294 | let dest_account_info = next_account_info(account_info_iter)?; 295 | let new_owner_account_info = next_account_info(account_info_iter)?; 296 | 297 | let mut dest_account_data = dest_account_info.data.borrow_mut(); 298 | if let State::Account(mut dest_account) = State::deserialize(&dest_account_data)? { 299 | if owner_account_info.key != &dest_account.owner { 300 | info!("Error: destination account owner is not present"); 301 | return Err(TokenError::NoOwner.into()); 302 | } 303 | if !owner_account_info.is_signer { 304 | info!("Error: owner account not a signer"); 305 | return Err(ProgramError::MissingRequiredSignature); 306 | } 307 | 308 | dest_account.owner = *new_owner_account_info.key; 309 | State::Account(dest_account).serialize(&mut dest_account_data)?; 310 | } else { 311 | info!("Error: destination account is invalid"); 312 | return Err(ProgramError::InvalidArgument); 313 | } 314 | Ok(()) 315 | } 316 | 317 | pub fn process( 318 | _program_id: &Pubkey, 319 | accounts: &'a [AccountInfo<'a>], 320 | input: &[u8], 321 | ) -> ProgramResult { 322 | let command = Command::deserialize(input)?; 323 | let account_info_iter = &mut accounts.iter(); 324 | 325 | match command { 326 | Command::NewToken(token_info) => { 327 | info!("Command: NewToken"); 328 | Self::process_newtoken(account_info_iter, token_info) 329 | } 330 | Command::NewTokenAccount => { 331 | info!("Command: NewTokenAccount"); 332 | Self::process_newaccount(account_info_iter) 333 | } 334 | Command::Transfer(amount) => { 335 | info!("Command: Transfer"); 336 | Self::process_transfer(account_info_iter, amount) 337 | } 338 | Command::Approve(amount) => { 339 | info!("Command: Approve"); 340 | Self::process_approve(account_info_iter, amount) 341 | } 342 | Command::SetOwner => { 343 | info!("Command: SetOwner"); 344 | Self::process_setowner(account_info_iter) 345 | } 346 | } 347 | } 348 | 349 | pub fn deserialize(input: &'a [u8]) -> Result { 350 | if input.len() < size_of::() { 351 | return Err(ProgramError::InvalidAccountData); 352 | } 353 | Ok(match input[0] { 354 | 0 => Self::Unallocated, 355 | 1 => { 356 | if input.len() < size_of::() + size_of::() { 357 | return Err(ProgramError::InvalidAccountData); 358 | } 359 | #[allow(clippy::cast_ptr_alignment)] 360 | let token: &Token = unsafe { &*(&input[1] as *const u8 as *const Token) }; 361 | Self::Token(*token) 362 | } 363 | 2 => { 364 | if input.len() < size_of::() + size_of::() { 365 | return Err(ProgramError::InvalidAccountData); 366 | } 367 | #[allow(clippy::cast_ptr_alignment)] 368 | let account: &TokenAccount = 369 | unsafe { &*(&input[1] as *const u8 as *const TokenAccount) }; 370 | Self::Account(*account) 371 | } 372 | 3 => Self::Invalid, 373 | _ => return Err(ProgramError::InvalidAccountData), 374 | }) 375 | } 376 | 377 | pub fn serialize(self: &Self, output: &mut [u8]) -> ProgramResult { 378 | if output.len() < size_of::() { 379 | return Err(ProgramError::InvalidAccountData); 380 | } 381 | match self { 382 | Self::Unallocated => output[0] = 0, 383 | Self::Token(token) => { 384 | if output.len() < size_of::() + size_of::() { 385 | return Err(ProgramError::InvalidAccountData); 386 | } 387 | output[0] = 1; 388 | #[allow(clippy::cast_ptr_alignment)] 389 | let value = unsafe { &mut *(&mut output[1] as *mut u8 as *mut Token) }; 390 | *value = *token; 391 | } 392 | Self::Account(account) => { 393 | if output.len() < size_of::() + size_of::() { 394 | return Err(ProgramError::InvalidAccountData); 395 | } 396 | output[0] = 2; 397 | #[allow(clippy::cast_ptr_alignment)] 398 | let value = unsafe { &mut *(&mut output[1] as *mut u8 as *mut TokenAccount) }; 399 | *value = *account; 400 | } 401 | Self::Invalid => output[0] = 3, 402 | } 403 | Ok(()) 404 | } 405 | } 406 | 407 | impl Command { 408 | pub fn deserialize(input: &[u8]) -> Result { 409 | if input.len() < size_of::() { 410 | return Err(ProgramError::InvalidAccountData); 411 | } 412 | Ok(match input[0] { 413 | 0 => { 414 | if input.len() < size_of::() + size_of::() { 415 | return Err(ProgramError::InvalidAccountData); 416 | } 417 | #[allow(clippy::cast_ptr_alignment)] 418 | let token: &Token = unsafe { &*(&input[1] as *const u8 as *const Token) }; 419 | Self::NewToken(*token) 420 | } 421 | 1 => Self::NewTokenAccount, 422 | 2 => { 423 | if input.len() < size_of::() + size_of::() { 424 | return Err(ProgramError::InvalidAccountData); 425 | } 426 | #[allow(clippy::cast_ptr_alignment)] 427 | let amount: &u64 = unsafe { &*(&input[1] as *const u8 as *const u64) }; 428 | Self::Transfer(*amount) 429 | } 430 | 3 => { 431 | if input.len() < size_of::() + size_of::() { 432 | return Err(ProgramError::InvalidAccountData); 433 | } 434 | #[allow(clippy::cast_ptr_alignment)] 435 | let amount: &u64 = unsafe { &*(&input[1] as *const u8 as *const u64) }; 436 | Self::Approve(*amount) 437 | } 438 | 4 => Self::SetOwner, 439 | _ => return Err(ProgramError::InvalidAccountData), 440 | }) 441 | } 442 | 443 | pub fn serialize(self: &Self, output: &mut [u8]) -> ProgramResult { 444 | if output.len() < size_of::() { 445 | return Err(ProgramError::InvalidAccountData); 446 | } 447 | match self { 448 | Self::NewToken(token) => { 449 | if output.len() < size_of::() + size_of::() { 450 | return Err(ProgramError::InvalidAccountData); 451 | } 452 | output[0] = 0; 453 | #[allow(clippy::cast_ptr_alignment)] 454 | let value = unsafe { &mut *(&mut output[1] as *mut u8 as *mut Token) }; 455 | *value = *token; 456 | } 457 | Self::NewTokenAccount => output[0] = 1, 458 | Self::Transfer(amount) => { 459 | if output.len() < size_of::() + size_of::() { 460 | return Err(ProgramError::InvalidAccountData); 461 | } 462 | output[0] = 2; 463 | #[allow(clippy::cast_ptr_alignment)] 464 | let value = unsafe { &mut *(&mut output[1] as *mut u8 as *mut u64) }; 465 | *value = *amount; 466 | } 467 | Self::Approve(amount) => { 468 | if output.len() < size_of::() + size_of::() { 469 | return Err(ProgramError::InvalidAccountData); 470 | } 471 | output[0] = 3; 472 | #[allow(clippy::cast_ptr_alignment)] 473 | let value = unsafe { &mut *(&mut output[1] as *mut u8 as *mut u64) }; 474 | *value = *amount; 475 | } 476 | Self::SetOwner => output[0] = 4, 477 | } 478 | Ok(()) 479 | } 480 | } 481 | 482 | // Pulls in the stubs required for `info!()` 483 | #[cfg(not(target_arch = "bpf"))] 484 | solana_sdk_bpf_test::stubs!(); 485 | 486 | #[cfg(test)] 487 | mod tests { 488 | use super::*; 489 | use solana_sdk::{account::Account, account_info::create_is_signer_account_infos}; 490 | 491 | fn new_pubkey(id: u8) -> Pubkey { 492 | Pubkey::new(&vec![ 493 | id, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 494 | 1, 1, 1, 495 | ]) 496 | } 497 | 498 | #[test] 499 | fn test_new_token() { 500 | let program_id = new_pubkey(1); 501 | let mut instruction_data = vec![0u8; size_of::()]; 502 | let token_account_key = new_pubkey(2); 503 | let mut token_account_account = Account::new(0, size_of::(), &program_id); 504 | let token_account2_key = new_pubkey(3); 505 | let mut token_account2_account = Account::new(0, size_of::(), &program_id); 506 | let delegate_account_key = new_pubkey(4); 507 | let mut delegate_account_account = Account::new(0, size_of::(), &program_id); 508 | let owner_key = new_pubkey(5); 509 | let mut owner_account = Account::default(); 510 | let token_key = new_pubkey(6); 511 | let mut token_account = Account::new(0, size_of::(), &program_id); 512 | let token2_key = new_pubkey(7); 513 | let mut token2_account = Account::new(0, size_of::(), &program_id); 514 | 515 | // token account not created 516 | let instruction = Command::NewToken(Token { 517 | supply: 1000, 518 | decimals: 2, 519 | }); 520 | instruction.serialize(&mut instruction_data).unwrap(); 521 | let mut accounts = vec![ 522 | (&token_key, true, &mut token_account), 523 | (&token_account_key, false, &mut token_account_account), 524 | ]; 525 | let mut account_infos = create_is_signer_account_infos(&mut accounts); 526 | assert_eq!( 527 | Err(ProgramError::InvalidArgument), 528 | State::process(&program_id, &mut account_infos, &instruction_data) 529 | ); 530 | 531 | // create token account 532 | let instruction = Command::NewTokenAccount; 533 | instruction.serialize(&mut instruction_data).unwrap(); 534 | let mut accounts = vec![ 535 | (&token_account_key, true, &mut token_account_account), 536 | (&owner_key, false, &mut owner_account), 537 | (&token_key, false, &mut token_account), 538 | ]; 539 | let mut account_infos = create_is_signer_account_infos(&mut accounts); 540 | State::process(&program_id, &mut account_infos, &instruction_data).unwrap(); 541 | 542 | // create new token 543 | let instruction = Command::NewToken(Token { 544 | supply: 1000, 545 | decimals: 2, 546 | }); 547 | instruction.serialize(&mut instruction_data).unwrap(); 548 | let mut accounts = vec![ 549 | (&token_key, true, &mut token_account), 550 | (&token_account_key, false, &mut token_account_account), 551 | ]; 552 | let mut account_infos = create_is_signer_account_infos(&mut accounts); 553 | State::process(&program_id, &mut account_infos, &instruction_data).unwrap(); 554 | 555 | // create another token account 556 | let instruction = Command::NewTokenAccount; 557 | instruction.serialize(&mut instruction_data).unwrap(); 558 | let mut accounts = vec![ 559 | (&token_account2_key, true, &mut token_account2_account), 560 | (&owner_key, false, &mut owner_account), 561 | (&token_key, false, &mut token_account), 562 | ]; 563 | let mut account_infos = create_is_signer_account_infos(&mut accounts); 564 | State::process(&program_id, &mut account_infos, &instruction_data).unwrap(); 565 | 566 | // token mismatch 567 | let instruction = Command::NewToken(Token { 568 | supply: 1000, 569 | decimals: 2, 570 | }); 571 | instruction.serialize(&mut instruction_data).unwrap(); 572 | let mut accounts = vec![ 573 | (&token2_key, true, &mut token2_account), 574 | (&token_account2_key, false, &mut token_account2_account), 575 | ]; 576 | let mut account_infos = create_is_signer_account_infos(&mut accounts); 577 | assert_eq!( 578 | Err(TokenError::TokenMismatch.into()), 579 | State::process(&program_id, &mut account_infos, &instruction_data) 580 | ); 581 | 582 | // create delegate account 583 | let instruction = Command::NewTokenAccount; 584 | instruction.serialize(&mut instruction_data).unwrap(); 585 | let mut accounts = vec![ 586 | (&delegate_account_key, true, &mut delegate_account_account), 587 | (&owner_key, false, &mut owner_account), 588 | (&token_key, false, &mut token_account), 589 | (&token_account_key, false, &mut token_account_account), 590 | ]; 591 | let mut account_infos = create_is_signer_account_infos(&mut accounts); 592 | State::process(&program_id, &mut account_infos, &instruction_data).unwrap(); 593 | 594 | // account is a delegate token 595 | let instruction = Command::NewToken(Token { 596 | supply: 1000, 597 | decimals: 2, 598 | }); 599 | instruction.serialize(&mut instruction_data).unwrap(); 600 | let mut accounts = vec![ 601 | (&token_key, true, &mut token_account), 602 | (&delegate_account_key, false, &mut delegate_account_account), 603 | ]; 604 | let mut account_infos = create_is_signer_account_infos(&mut accounts); 605 | assert_eq!( 606 | Err(ProgramError::InvalidArgument), 607 | State::process(&program_id, &mut account_infos, &instruction_data) 608 | ); 609 | 610 | // create twice 611 | let mut accounts = vec![ 612 | (&token_key, true, &mut token_account), 613 | (&token_account_key, false, &mut token_account_account), 614 | ]; 615 | let mut account_infos = create_is_signer_account_infos(&mut accounts); 616 | assert_eq!( 617 | Err(ProgramError::InvalidArgument), 618 | State::process(&program_id, &mut account_infos, &instruction_data) 619 | ); 620 | } 621 | 622 | #[test] 623 | fn test_new_token_account() { 624 | let program_id = new_pubkey(1); 625 | let mut instruction_data = vec![0u8; size_of::()]; 626 | let token_account_key = new_pubkey(2); 627 | let mut token_account_account = Account::new(0, size_of::(), &program_id); 628 | let owner_key = new_pubkey(3); 629 | let mut owner_account = Account::default(); 630 | let token_key = new_pubkey(4); 631 | let mut token_account = Account::new(0, size_of::(), &program_id); 632 | 633 | // missing signer 634 | let instruction = Command::NewTokenAccount; 635 | instruction.serialize(&mut instruction_data).unwrap(); 636 | let mut accounts = vec![ 637 | (&token_account_key, false, &mut token_account_account), 638 | (&owner_key, false, &mut owner_account), 639 | (&token_key, false, &mut token_account), 640 | ]; 641 | let mut account_infos = create_is_signer_account_infos(&mut accounts); 642 | assert_eq!( 643 | Err(ProgramError::MissingRequiredSignature), 644 | State::process(&program_id, &mut account_infos, &instruction_data) 645 | ); 646 | 647 | // create token account 648 | let mut accounts = vec![ 649 | (&token_account_key, true, &mut token_account_account), 650 | (&owner_key, false, &mut owner_account), 651 | (&token_key, false, &mut token_account), 652 | ]; 653 | let mut account_infos = create_is_signer_account_infos(&mut accounts); 654 | State::process(&program_id, &mut account_infos, &instruction_data).unwrap(); 655 | 656 | // create twice 657 | let mut accounts = vec![ 658 | (&token_account_key, true, &mut token_account_account), 659 | (&owner_key, false, &mut owner_account), 660 | (&token_key, false, &mut token_account), 661 | ]; 662 | let mut account_infos = create_is_signer_account_infos(&mut accounts); 663 | assert_eq!( 664 | Err(ProgramError::InvalidArgument), 665 | State::process(&program_id, &mut account_infos, &instruction_data) 666 | ); 667 | } 668 | 669 | #[test] 670 | fn test_transfer() { 671 | let program_id = new_pubkey(1); 672 | let mut instruction_data = vec![0u8; size_of::()]; 673 | let token_account_key = new_pubkey(2); 674 | let mut token_account_account = Account::new(0, size_of::(), &program_id); 675 | let token_account2_key = new_pubkey(3); 676 | let mut token_account2_account = Account::new(0, size_of::(), &program_id); 677 | let token_account3_key = new_pubkey(3); 678 | let mut token_account3_account = Account::new(0, size_of::(), &program_id); 679 | let delegate_account_key = new_pubkey(4); 680 | let mut delegate_account_account = Account::new(0, size_of::(), &program_id); 681 | let mismatch_account_key = new_pubkey(5); 682 | let mut mismatch_account_account = Account::new(0, size_of::(), &program_id); 683 | let mismatch_delegate_account_key = new_pubkey(5); 684 | let mut mismatch_delegate_account_account = 685 | Account::new(0, size_of::(), &program_id); 686 | let owner_key = new_pubkey(6); 687 | let mut owner_account = Account::default(); 688 | let owner2_key = new_pubkey(7); 689 | let mut owner2_account = Account::default(); 690 | let token_key = new_pubkey(8); 691 | let mut token_account = Account::new(0, size_of::(), &program_id); 692 | let token2_key = new_pubkey(9); 693 | let mut token2_account = Account::new(0, size_of::(), &program_id); 694 | 695 | // create token account 696 | let instruction = Command::NewTokenAccount; 697 | instruction.serialize(&mut instruction_data).unwrap(); 698 | let mut accounts = vec![ 699 | (&token_account_key, true, &mut token_account_account), 700 | (&owner_key, false, &mut owner_account), 701 | (&token_key, false, &mut token_account), 702 | ]; 703 | let mut account_infos = create_is_signer_account_infos(&mut accounts); 704 | State::process(&program_id, &mut account_infos, &instruction_data).unwrap(); 705 | 706 | // create another token account 707 | let instruction = Command::NewTokenAccount; 708 | instruction.serialize(&mut instruction_data).unwrap(); 709 | let mut accounts = vec![ 710 | (&token_account2_key, true, &mut token_account2_account), 711 | (&owner_key, false, &mut owner_account), 712 | (&token_key, false, &mut token_account), 713 | ]; 714 | let mut account_infos = create_is_signer_account_infos(&mut accounts); 715 | State::process(&program_id, &mut account_infos, &instruction_data).unwrap(); 716 | 717 | // create another token account 718 | let instruction = Command::NewTokenAccount; 719 | instruction.serialize(&mut instruction_data).unwrap(); 720 | let mut accounts = vec![ 721 | (&token_account3_key, true, &mut token_account3_account), 722 | (&owner_key, false, &mut owner_account), 723 | (&token_key, false, &mut token_account), 724 | ]; 725 | let mut account_infos = create_is_signer_account_infos(&mut accounts); 726 | State::process(&program_id, &mut account_infos, &instruction_data).unwrap(); 727 | 728 | // create mismatch token account 729 | let instruction = Command::NewTokenAccount; 730 | instruction.serialize(&mut instruction_data).unwrap(); 731 | let mut accounts = vec![ 732 | (&mismatch_account_key, true, &mut mismatch_account_account), 733 | (&owner_key, false, &mut owner_account), 734 | (&token2_key, false, &mut token2_account), 735 | ]; 736 | let mut account_infos = create_is_signer_account_infos(&mut accounts); 737 | State::process(&program_id, &mut account_infos, &instruction_data).unwrap(); 738 | 739 | // create delegate account 740 | let instruction = Command::NewTokenAccount; 741 | instruction.serialize(&mut instruction_data).unwrap(); 742 | let mut accounts = vec![ 743 | (&delegate_account_key, true, &mut delegate_account_account), 744 | (&owner_key, false, &mut owner_account), 745 | (&token_key, false, &mut token_account), 746 | (&token_account_key, false, &mut token_account_account), 747 | ]; 748 | let mut account_infos = create_is_signer_account_infos(&mut accounts); 749 | State::process(&program_id, &mut account_infos, &instruction_data).unwrap(); 750 | 751 | // create mismatch delegate account 752 | let instruction = Command::NewTokenAccount; 753 | instruction.serialize(&mut instruction_data).unwrap(); 754 | let mut accounts = vec![ 755 | ( 756 | &mismatch_delegate_account_key, 757 | true, 758 | &mut mismatch_delegate_account_account, 759 | ), 760 | (&owner_key, false, &mut owner_account), 761 | (&token2_key, false, &mut token2_account), 762 | (&token_account_key, false, &mut token_account_account), 763 | ]; 764 | let mut account_infos = create_is_signer_account_infos(&mut accounts); 765 | State::process(&program_id, &mut account_infos, &instruction_data).unwrap(); 766 | 767 | // create new token 768 | let instruction = Command::NewToken(Token { 769 | supply: 1000, 770 | decimals: 2, 771 | }); 772 | instruction.serialize(&mut instruction_data).unwrap(); 773 | let mut accounts = vec![ 774 | (&token_key, true, &mut token_account), 775 | (&token_account_key, false, &mut token_account_account), 776 | ]; 777 | let mut account_infos = create_is_signer_account_infos(&mut accounts); 778 | State::process(&program_id, &mut account_infos, &instruction_data).unwrap(); 779 | 780 | // missing signer 781 | let instruction = Command::Transfer(1000); 782 | instruction.serialize(&mut instruction_data).unwrap(); 783 | let mut accounts = vec![ 784 | (&owner_key, false, &mut owner_account), 785 | (&token_account_key, false, &mut token_account_account), 786 | (&token_account2_key, false, &mut token_account2_account), 787 | ]; 788 | let mut account_infos = create_is_signer_account_infos(&mut accounts); 789 | assert_eq!( 790 | Err(ProgramError::MissingRequiredSignature), 791 | State::process(&program_id, &mut account_infos, &instruction_data) 792 | ); 793 | 794 | // destination is delegate 795 | let instruction = Command::Transfer(1000); 796 | instruction.serialize(&mut instruction_data).unwrap(); 797 | let mut accounts = vec![ 798 | (&owner_key, true, &mut owner_account), 799 | (&token_account2_key, false, &mut token_account2_account), 800 | (&delegate_account_key, false, &mut delegate_account_account), 801 | ]; 802 | let mut account_infos = create_is_signer_account_infos(&mut accounts); 803 | assert_eq!( 804 | Err(ProgramError::InvalidArgument), 805 | State::process(&program_id, &mut account_infos, &instruction_data) 806 | ); 807 | 808 | // mismatch token 809 | let instruction = Command::Transfer(1000); 810 | instruction.serialize(&mut instruction_data).unwrap(); 811 | let mut accounts = vec![ 812 | (&owner_key, true, &mut owner_account), 813 | (&token_account2_key, false, &mut token_account2_account), 814 | (&mismatch_account_key, false, &mut mismatch_account_account), 815 | ]; 816 | let mut account_infos = create_is_signer_account_infos(&mut accounts); 817 | assert_eq!( 818 | Err(TokenError::TokenMismatch.into()), 819 | State::process(&program_id, &mut account_infos, &instruction_data) 820 | ); 821 | 822 | // missing owner 823 | let instruction = Command::Transfer(1000); 824 | instruction.serialize(&mut instruction_data).unwrap(); 825 | let mut accounts = vec![ 826 | (&owner2_key, true, &mut owner2_account), 827 | (&token_account_key, false, &mut token_account_account), 828 | (&token_account2_key, false, &mut token_account2_account), 829 | ]; 830 | let mut account_infos = create_is_signer_account_infos(&mut accounts); 831 | assert_eq!( 832 | Err(TokenError::NoOwner.into()), 833 | State::process(&program_id, &mut account_infos, &instruction_data) 834 | ); 835 | 836 | // transfer 837 | let instruction = Command::Transfer(1000); 838 | instruction.serialize(&mut instruction_data).unwrap(); 839 | let mut accounts = vec![ 840 | (&owner_key, true, &mut owner_account), 841 | (&token_account_key, false, &mut token_account_account), 842 | (&token_account2_key, false, &mut token_account2_account), 843 | ]; 844 | let mut account_infos = create_is_signer_account_infos(&mut accounts); 845 | State::process(&program_id, &mut account_infos, &instruction_data).unwrap(); 846 | 847 | // insufficient funds 848 | let instruction = Command::Transfer(1); 849 | instruction.serialize(&mut instruction_data).unwrap(); 850 | let mut accounts = vec![ 851 | (&owner_key, true, &mut owner_account), 852 | (&token_account_key, false, &mut token_account_account), 853 | (&token_account2_key, false, &mut token_account2_account), 854 | ]; 855 | let mut account_infos = create_is_signer_account_infos(&mut accounts); 856 | assert_eq!( 857 | Err(TokenError::InsufficientFunds.into()), 858 | State::process(&program_id, &mut account_infos, &instruction_data) 859 | ); 860 | 861 | // transfer half back 862 | let instruction = Command::Transfer(500); 863 | instruction.serialize(&mut instruction_data).unwrap(); 864 | let mut accounts = vec![ 865 | (&owner_key, true, &mut owner_account), 866 | (&token_account2_key, false, &mut token_account2_account), 867 | (&token_account_key, false, &mut token_account_account), 868 | ]; 869 | let mut account_infos = create_is_signer_account_infos(&mut accounts); 870 | State::process(&program_id, &mut account_infos, &instruction_data).unwrap(); 871 | 872 | // transfer rest 873 | let instruction = Command::Transfer(500); 874 | instruction.serialize(&mut instruction_data).unwrap(); 875 | let mut accounts = vec![ 876 | (&owner_key, true, &mut owner_account), 877 | (&token_account2_key, false, &mut token_account2_account), 878 | (&token_account_key, false, &mut token_account_account), 879 | ]; 880 | let mut account_infos = create_is_signer_account_infos(&mut accounts); 881 | State::process(&program_id, &mut account_infos, &instruction_data).unwrap(); 882 | 883 | // insufficient funds 884 | let instruction = Command::Transfer(1); 885 | instruction.serialize(&mut instruction_data).unwrap(); 886 | let mut accounts = vec![ 887 | (&owner_key, true, &mut owner_account), 888 | (&token_account2_key, false, &mut token_account2_account), 889 | (&token_account_key, false, &mut token_account_account), 890 | ]; 891 | let mut account_infos = create_is_signer_account_infos(&mut accounts); 892 | assert_eq!( 893 | Err(TokenError::InsufficientFunds.into()), 894 | State::process(&program_id, &mut account_infos, &instruction_data) 895 | ); 896 | 897 | // approve delegate 898 | let instruction = Command::Approve(100); 899 | instruction.serialize(&mut instruction_data).unwrap(); 900 | let mut accounts = vec![ 901 | (&owner_key, true, &mut owner_account), 902 | (&token_account_key, false, &mut token_account_account), 903 | (&delegate_account_key, false, &mut delegate_account_account), 904 | ]; 905 | let mut account_infos = create_is_signer_account_infos(&mut accounts); 906 | State::process(&program_id, &mut account_infos, &instruction_data).unwrap(); 907 | 908 | // not a delegate of source account 909 | let instruction = Command::Transfer(100); 910 | instruction.serialize(&mut instruction_data).unwrap(); 911 | let mut accounts = vec![ 912 | (&owner_key, true, &mut owner_account), 913 | (&delegate_account_key, false, &mut delegate_account_account), 914 | (&token_account2_key, false, &mut token_account2_account), 915 | (&token_account3_key, false, &mut token_account3_account), 916 | ]; 917 | let mut account_infos = create_is_signer_account_infos(&mut accounts); 918 | assert_eq!( 919 | Err(TokenError::NotDelegate.into()), 920 | State::process(&program_id, &mut account_infos, &instruction_data) 921 | ); 922 | 923 | // transfer via delegate 924 | let instruction = Command::Transfer(100); 925 | instruction.serialize(&mut instruction_data).unwrap(); 926 | let mut accounts = vec![ 927 | (&owner_key, true, &mut owner_account), 928 | (&delegate_account_key, false, &mut delegate_account_account), 929 | (&token_account2_key, false, &mut token_account2_account), 930 | (&token_account_key, false, &mut token_account_account), 931 | ]; 932 | let mut account_infos = create_is_signer_account_infos(&mut accounts); 933 | State::process(&program_id, &mut account_infos, &instruction_data).unwrap(); 934 | 935 | // insufficient funds approved via delegate 936 | let instruction = Command::Transfer(100); 937 | instruction.serialize(&mut instruction_data).unwrap(); 938 | let mut accounts = vec![ 939 | (&owner_key, true, &mut owner_account), 940 | (&delegate_account_key, false, &mut delegate_account_account), 941 | (&token_account2_key, false, &mut token_account2_account), 942 | (&token_account_key, false, &mut token_account_account), 943 | ]; 944 | let mut account_infos = create_is_signer_account_infos(&mut accounts); 945 | assert_eq!( 946 | Err(TokenError::InsufficientFunds.into()), 947 | State::process(&program_id, &mut account_infos, &instruction_data) 948 | ); 949 | 950 | // transfer rest 951 | let instruction = Command::Transfer(900); 952 | instruction.serialize(&mut instruction_data).unwrap(); 953 | let mut accounts = vec![ 954 | (&owner_key, true, &mut owner_account), 955 | (&token_account_key, false, &mut token_account_account), 956 | (&token_account2_key, false, &mut token_account2_account), 957 | ]; 958 | let mut account_infos = create_is_signer_account_infos(&mut accounts); 959 | State::process(&program_id, &mut account_infos, &instruction_data).unwrap(); 960 | 961 | // approve delegate 962 | let instruction = Command::Approve(100); 963 | instruction.serialize(&mut instruction_data).unwrap(); 964 | let mut accounts = vec![ 965 | (&owner_key, true, &mut owner_account), 966 | (&token_account_key, false, &mut token_account_account), 967 | (&delegate_account_key, false, &mut delegate_account_account), 968 | ]; 969 | let mut account_infos = create_is_signer_account_infos(&mut accounts); 970 | State::process(&program_id, &mut account_infos, &instruction_data).unwrap(); 971 | 972 | // insufficient funds in source account via delegate 973 | let instruction = Command::Transfer(100); 974 | instruction.serialize(&mut instruction_data).unwrap(); 975 | let mut accounts = vec![ 976 | (&owner_key, true, &mut owner_account), 977 | (&delegate_account_key, false, &mut delegate_account_account), 978 | (&token_account2_key, false, &mut token_account2_account), 979 | (&token_account_key, false, &mut token_account_account), 980 | ]; 981 | let mut account_infos = create_is_signer_account_infos(&mut accounts); 982 | assert_eq!( 983 | Err(TokenError::InsufficientFunds.into()), 984 | State::process(&program_id, &mut account_infos, &instruction_data) 985 | ); 986 | } 987 | 988 | #[test] 989 | fn test_approve() { 990 | let program_id = new_pubkey(1); 991 | let mut instruction_data = vec![0u8; size_of::()]; 992 | let token_account_key = new_pubkey(2); 993 | let mut token_account_account = Account::new(0, size_of::(), &program_id); 994 | let token_account2_key = new_pubkey(3); 995 | let mut token_account2_account = Account::new(0, size_of::(), &program_id); 996 | let delegate_account_key = new_pubkey(4); 997 | let mut delegate_account_account = Account::new(0, size_of::(), &program_id); 998 | let mismatch_delegate_account_key = new_pubkey(5); 999 | let mut mismatch_delegate_account_account = 1000 | Account::new(0, size_of::(), &program_id); 1001 | let owner_key = new_pubkey(6); 1002 | let mut owner_account = Account::default(); 1003 | let owner2_key = new_pubkey(7); 1004 | let mut owner2_account = Account::default(); 1005 | let token_key = new_pubkey(8); 1006 | let mut token_account = Account::new(0, size_of::(), &program_id); 1007 | let token2_key = new_pubkey(9); 1008 | let mut token2_account = Account::new(0, size_of::(), &program_id); 1009 | 1010 | // create token account 1011 | let instruction = Command::NewTokenAccount; 1012 | instruction.serialize(&mut instruction_data).unwrap(); 1013 | let mut accounts = vec![ 1014 | (&token_account_key, true, &mut token_account_account), 1015 | (&owner_key, false, &mut owner_account), 1016 | (&token_key, false, &mut token_account), 1017 | ]; 1018 | let mut account_infos = create_is_signer_account_infos(&mut accounts); 1019 | State::process(&program_id, &mut account_infos, &instruction_data).unwrap(); 1020 | 1021 | // create another token account 1022 | let instruction = Command::NewTokenAccount; 1023 | instruction.serialize(&mut instruction_data).unwrap(); 1024 | let mut accounts = vec![ 1025 | (&token_account2_key, true, &mut token_account2_account), 1026 | (&owner_key, false, &mut owner_account), 1027 | (&token_key, false, &mut token_account), 1028 | ]; 1029 | let mut account_infos = create_is_signer_account_infos(&mut accounts); 1030 | State::process(&program_id, &mut account_infos, &instruction_data).unwrap(); 1031 | 1032 | // create delegate account 1033 | let instruction = Command::NewTokenAccount; 1034 | instruction.serialize(&mut instruction_data).unwrap(); 1035 | let mut accounts = vec![ 1036 | (&delegate_account_key, true, &mut delegate_account_account), 1037 | (&owner_key, false, &mut owner_account), 1038 | (&token_key, false, &mut token_account), 1039 | (&token_account_key, false, &mut token_account_account), 1040 | ]; 1041 | let mut account_infos = create_is_signer_account_infos(&mut accounts); 1042 | State::process(&program_id, &mut account_infos, &instruction_data).unwrap(); 1043 | 1044 | // create mismatch delegate account 1045 | let instruction = Command::NewTokenAccount; 1046 | instruction.serialize(&mut instruction_data).unwrap(); 1047 | let mut accounts = vec![ 1048 | ( 1049 | &mismatch_delegate_account_key, 1050 | true, 1051 | &mut mismatch_delegate_account_account, 1052 | ), 1053 | (&owner_key, false, &mut owner_account), 1054 | (&token2_key, false, &mut token2_account), 1055 | (&token_account_key, false, &mut token_account_account), 1056 | ]; 1057 | let mut account_infos = create_is_signer_account_infos(&mut accounts); 1058 | State::process(&program_id, &mut account_infos, &instruction_data).unwrap(); 1059 | 1060 | // create new token 1061 | let instruction = Command::NewToken(Token { 1062 | supply: 1000, 1063 | decimals: 2, 1064 | }); 1065 | instruction.serialize(&mut instruction_data).unwrap(); 1066 | let mut accounts = vec![ 1067 | (&token_key, true, &mut token_account), 1068 | (&token_account_key, false, &mut token_account_account), 1069 | ]; 1070 | let mut account_infos = create_is_signer_account_infos(&mut accounts); 1071 | State::process(&program_id, &mut account_infos, &instruction_data).unwrap(); 1072 | 1073 | // token mismatch 1074 | let instruction = Command::Approve(100); 1075 | instruction.serialize(&mut instruction_data).unwrap(); 1076 | let mut accounts = vec![ 1077 | (&owner_key, true, &mut owner_account), 1078 | (&token_account_key, false, &mut token_account_account), 1079 | ( 1080 | &mismatch_delegate_account_key, 1081 | false, 1082 | &mut mismatch_delegate_account_account, 1083 | ), 1084 | ]; 1085 | let mut account_infos = create_is_signer_account_infos(&mut accounts); 1086 | assert_eq!( 1087 | Err(TokenError::TokenMismatch.into()), 1088 | State::process(&program_id, &mut account_infos, &instruction_data) 1089 | ); 1090 | 1091 | // missing signer 1092 | let instruction = Command::Approve(100); 1093 | instruction.serialize(&mut instruction_data).unwrap(); 1094 | let mut accounts = vec![ 1095 | (&owner_key, false, &mut owner_account), 1096 | (&token_account_key, false, &mut token_account_account), 1097 | (&delegate_account_key, false, &mut delegate_account_account), 1098 | ]; 1099 | let mut account_infos = create_is_signer_account_infos(&mut accounts); 1100 | assert_eq!( 1101 | Err(ProgramError::MissingRequiredSignature), 1102 | State::process(&program_id, &mut account_infos, &instruction_data) 1103 | ); 1104 | 1105 | // missing signer 1106 | let instruction = Command::Approve(100); 1107 | instruction.serialize(&mut instruction_data).unwrap(); 1108 | let mut accounts = vec![ 1109 | (&owner2_key, true, &mut owner2_account), 1110 | (&token_account_key, false, &mut token_account_account), 1111 | (&delegate_account_key, false, &mut delegate_account_account), 1112 | ]; 1113 | let mut account_infos = create_is_signer_account_infos(&mut accounts); 1114 | assert_eq!( 1115 | Err(TokenError::NoOwner.into()), 1116 | State::process(&program_id, &mut account_infos, &instruction_data) 1117 | ); 1118 | 1119 | // destination is delegate 1120 | let instruction = Command::Approve(100); 1121 | instruction.serialize(&mut instruction_data).unwrap(); 1122 | let mut accounts = vec![ 1123 | (&owner_key, true, &mut owner_account), 1124 | (&delegate_account_key, false, &mut delegate_account_account), 1125 | (&token_account_key, false, &mut token_account_account), 1126 | ]; 1127 | let mut account_infos = create_is_signer_account_infos(&mut accounts); 1128 | assert_eq!( 1129 | Err(ProgramError::InvalidArgument), 1130 | State::process(&program_id, &mut account_infos, &instruction_data) 1131 | ); 1132 | 1133 | // not a delegate 1134 | let instruction = Command::Approve(100); 1135 | instruction.serialize(&mut instruction_data).unwrap(); 1136 | let mut accounts = vec![ 1137 | (&owner_key, true, &mut owner_account), 1138 | (&token_account2_key, false, &mut token_account2_account), 1139 | (&token_account_key, false, &mut token_account_account), 1140 | ]; 1141 | let mut account_infos = create_is_signer_account_infos(&mut accounts); 1142 | assert_eq!( 1143 | Err(TokenError::NotDelegate.into()), 1144 | State::process(&program_id, &mut account_infos, &instruction_data) 1145 | ); 1146 | 1147 | // not a delegate of source 1148 | let instruction = Command::Approve(100); 1149 | instruction.serialize(&mut instruction_data).unwrap(); 1150 | let mut accounts = vec![ 1151 | (&owner_key, true, &mut owner_account), 1152 | (&token_account2_key, false, &mut token_account2_account), 1153 | (&delegate_account_key, false, &mut delegate_account_account), 1154 | ]; 1155 | let mut account_infos = create_is_signer_account_infos(&mut accounts); 1156 | assert_eq!( 1157 | Err(TokenError::NotDelegate.into()), 1158 | State::process(&program_id, &mut account_infos, &instruction_data) 1159 | ); 1160 | 1161 | // approve delegate 1162 | let instruction = Command::Approve(100); 1163 | instruction.serialize(&mut instruction_data).unwrap(); 1164 | let mut accounts = vec![ 1165 | (&owner_key, true, &mut owner_account), 1166 | (&token_account_key, false, &mut token_account_account), 1167 | (&delegate_account_key, false, &mut delegate_account_account), 1168 | ]; 1169 | let mut account_infos = create_is_signer_account_infos(&mut accounts); 1170 | State::process(&program_id, &mut account_infos, &instruction_data).unwrap(); 1171 | } 1172 | 1173 | #[test] 1174 | fn test_set_owner() { 1175 | let program_id = new_pubkey(1); 1176 | let mut instruction_data = vec![0u8; size_of::()]; 1177 | let token_account_key = new_pubkey(2); 1178 | let mut token_account_account = Account::new(0, size_of::(), &program_id); 1179 | let owner_key = new_pubkey(3); 1180 | let mut owner_account = Account::default(); 1181 | let owner2_key = new_pubkey(4); 1182 | let mut owner2_account = Account::default(); 1183 | let owner3_key = new_pubkey(5); 1184 | let mut owner3_account = Account::default(); 1185 | let token_key = new_pubkey(6); 1186 | let mut token_account = Account::new(0, size_of::(), &program_id); 1187 | 1188 | // invalid token account 1189 | let instruction = Command::SetOwner; 1190 | instruction.serialize(&mut instruction_data).unwrap(); 1191 | let mut accounts = vec![ 1192 | (&owner_key, false, &mut owner_account), 1193 | (&token_account_key, false, &mut token_account_account), 1194 | (&owner2_key, false, &mut owner2_account), 1195 | ]; 1196 | let mut account_infos = create_is_signer_account_infos(&mut accounts); 1197 | assert_eq!( 1198 | Err(ProgramError::InvalidArgument), 1199 | State::process(&program_id, &mut account_infos, &instruction_data) 1200 | ); 1201 | 1202 | // create token account 1203 | let instruction = Command::NewTokenAccount; 1204 | instruction.serialize(&mut instruction_data).unwrap(); 1205 | let mut accounts = vec![ 1206 | (&token_account_key, true, &mut token_account_account), 1207 | (&owner_key, false, &mut owner_account), 1208 | (&token_key, false, &mut token_account), 1209 | ]; 1210 | let mut account_infos = create_is_signer_account_infos(&mut accounts); 1211 | State::process(&program_id, &mut account_infos, &instruction_data).unwrap(); 1212 | 1213 | // missing owner 1214 | let instruction = Command::SetOwner; 1215 | instruction.serialize(&mut instruction_data).unwrap(); 1216 | let mut accounts = vec![ 1217 | (&owner2_key, false, &mut owner2_account), 1218 | (&token_account_key, false, &mut token_account_account), 1219 | (&owner3_key, false, &mut owner3_account), 1220 | ]; 1221 | let mut account_infos = create_is_signer_account_infos(&mut accounts); 1222 | assert_eq!( 1223 | Err(TokenError::NoOwner.into()), 1224 | State::process(&program_id, &mut account_infos, &instruction_data) 1225 | ); 1226 | 1227 | // owner did not sign 1228 | let mut accounts = vec![ 1229 | (&owner_key, false, &mut owner_account), 1230 | (&token_account_key, true, &mut token_account_account), 1231 | (&owner2_key, false, &mut owner2_account), 1232 | ]; 1233 | let mut account_infos = create_is_signer_account_infos(&mut accounts); 1234 | assert_eq!( 1235 | Err(ProgramError::MissingRequiredSignature), 1236 | State::process(&program_id, &mut account_infos, &instruction_data) 1237 | ); 1238 | 1239 | // set owner 1240 | let mut accounts = vec![ 1241 | (&owner_key, true, &mut owner_account), 1242 | (&token_account_key, true, &mut token_account_account), 1243 | (&owner2_key, false, &mut owner2_account), 1244 | ]; 1245 | let mut account_infos = create_is_signer_account_infos(&mut accounts); 1246 | State::process(&program_id, &mut account_infos, &instruction_data).unwrap(); 1247 | } 1248 | } 1249 | -------------------------------------------------------------------------------- /url.js: -------------------------------------------------------------------------------- 1 | // To connect to a public cluster, set `export LIVE=1` in your 2 | // environment. By default, `LIVE=1` will connect to the devnet cluster. 3 | 4 | import {clusterApiUrl, Cluster} from '@solana/web3.js'; 5 | import dotenv from 'dotenv'; 6 | 7 | function chooseCluster(): Cluster | undefined { 8 | dotenv.config(); 9 | if (!process.env.LIVE) return; 10 | switch (process.env.CLUSTER) { 11 | case 'devnet': 12 | case 'testnet': 13 | case 'mainnet-beta': { 14 | return process.env.CLUSTER; 15 | } 16 | } 17 | throw 'Unknown cluster "' + process.env.CLUSTER + '", check the .env file'; 18 | } 19 | 20 | export const cluster = chooseCluster(); 21 | 22 | export const url = 23 | process.env.RPC_URL || 24 | (process.env.LIVE ? clusterApiUrl(cluster, false) : 'http://localhost:8899'); 25 | 26 | export const urlTls = 27 | process.env.RPC_URL || 28 | (process.env.LIVE ? clusterApiUrl(cluster, true) : 'http://localhost:8899'); 29 | 30 | export let walletUrl = 31 | process.env.WALLET_URL || 'https://solana-example-webwallet.herokuapp.com/'; 32 | --------------------------------------------------------------------------------