initial import

This commit is contained in:
Andrea Franz 2020-02-18 20:23:31 +01:00
commit 55dbd113f9
No known key found for this signature in database
GPG Key ID: 4F0D2F2D9DE7F29D
30 changed files with 6469 additions and 0 deletions

8
.gitignore vendored Normal file
View File

@ -0,0 +1,8 @@
.embark
chains.json
config/livenet/password
config/production/password
coverage
dist
embarkArtifacts
node_modules

0
.npmrc Normal file
View File

357
CHANGELOG.md Normal file
View File

@ -0,0 +1,357 @@
# Change Log
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [5.1.1](https://github.com/embarklabs/embark/compare/v5.1.1-nightly.4...v5.1.1) (2020-02-03)
**Note:** Version bump only for package embark-dapp-template-boilerplate
## [5.1.1-nightly.4](https://github.com/embarklabs/embark/compare/v5.1.1-nightly.3...v5.1.1-nightly.4) (2020-02-03)
**Note:** Version bump only for package embark-dapp-template-boilerplate
## [5.1.1-nightly.2](https://github.com/embarklabs/embark/compare/v5.1.1-nightly.1...v5.1.1-nightly.2) (2020-01-31)
**Note:** Version bump only for package embark-dapp-template-boilerplate
## [5.1.1-nightly.1](https://github.com/embarklabs/embark/compare/v5.1.1-nightly.0...v5.1.1-nightly.1) (2020-01-30)
**Note:** Version bump only for package embark-dapp-template-boilerplate
## [5.1.1-nightly.0](https://github.com/embarklabs/embark/compare/v5.1.0...v5.1.1-nightly.0) (2020-01-29)
**Note:** Version bump only for package embark-dapp-template-boilerplate
# [5.1.0](https://github.com/embarklabs/embark/compare/v5.1.0-nightly.6...v5.1.0) (2020-01-27)
**Note:** Version bump only for package embark-dapp-template-boilerplate
# [5.1.0-nightly.6](https://github.com/embarklabs/embark/compare/v5.1.0-nightly.5...v5.1.0-nightly.6) (2020-01-25)
**Note:** Version bump only for package embark-dapp-template-boilerplate
# [5.1.0-nightly.5](https://github.com/embarklabs/embark/compare/v5.1.0-nightly.4...v5.1.0-nightly.5) (2020-01-24)
**Note:** Version bump only for package embark-dapp-template-boilerplate
# [5.1.0-nightly.4](https://github.com/embarklabs/embark/compare/v5.1.0-nightly.3...v5.1.0-nightly.4) (2020-01-23)
**Note:** Version bump only for package embark-dapp-template-boilerplate
# [5.1.0-nightly.3](https://github.com/embarklabs/embark/compare/v5.1.0-nightly.2...v5.1.0-nightly.3) (2020-01-22)
**Note:** Version bump only for package embark-dapp-template-boilerplate
# [5.1.0-nightly.2](https://github.com/embarklabs/embark/compare/v5.1.0-nightly.1...v5.1.0-nightly.2) (2020-01-21)
**Note:** Version bump only for package embark-dapp-template-boilerplate
# [5.1.0-nightly.1](https://github.com/embarklabs/embark/compare/v5.1.0-nightly.0...v5.1.0-nightly.1) (2020-01-20)
**Note:** Version bump only for package embark-dapp-template-boilerplate
# [5.1.0-nightly.0](https://github.com/embarklabs/embark/compare/v5.0.0...v5.1.0-nightly.0) (2020-01-17)
**Note:** Version bump only for package embark-dapp-template-boilerplate
# [5.0.0](https://github.com/embarklabs/embark/compare/v5.0.0-beta.0...v5.0.0) (2020-01-07)
**Note:** Version bump only for package embark-dapp-template-boilerplate
# [5.0.0-beta.0](https://github.com/embarklabs/embark/compare/v5.0.0-alpha.10...v5.0.0-beta.0) (2020-01-03)
**Note:** Version bump only for package embark-dapp-template-boilerplate
# [5.0.0-alpha.10](https://github.com/embarklabs/embark/compare/v5.0.0-alpha.9...v5.0.0-alpha.10) (2019-12-24)
**Note:** Version bump only for package embark-dapp-template-boilerplate
# [5.0.0-alpha.9](https://github.com/embarklabs/embark/compare/v5.0.0-alpha.8...v5.0.0-alpha.9) (2019-12-20)
**Note:** Version bump only for package embark-dapp-template-boilerplate
# [5.0.0-alpha.8](https://github.com/embarklabs/embark/compare/v5.0.0-alpha.7...v5.0.0-alpha.8) (2019-12-19)
**Note:** Version bump only for package embark-dapp-template-boilerplate
# [5.0.0-alpha.7](https://github.com/embarklabs/embark/compare/v5.0.0-alpha.6...v5.0.0-alpha.7) (2019-12-18)
**Note:** Version bump only for package embark-dapp-template-boilerplate
# [5.0.0-alpha.6](https://github.com/embarklabs/embark/compare/v5.0.0-alpha.5...v5.0.0-alpha.6) (2019-12-17)
**Note:** Version bump only for package embark-dapp-template-boilerplate
# [5.0.0-alpha.5](https://github.com/embarklabs/embark/compare/v5.0.0-alpha.4...v5.0.0-alpha.5) (2019-12-16)
**Note:** Version bump only for package embark-dapp-template-boilerplate
# [5.0.0-alpha.4](https://github.com/embarklabs/embark/compare/v5.0.0-alpha.3...v5.0.0-alpha.4) (2019-12-12)
### Features
* **@embark/whisper:** Add Whisper client config ([bd4b110](https://github.com/embarklabs/embark/commit/bd4b110))
# [5.0.0-alpha.3](https://github.com/embarklabs/embark/compare/v5.0.0-alpha.2...v5.0.0-alpha.3) (2019-12-06)
**Note:** Version bump only for package embark-dapp-template-boilerplate
# [5.0.0-alpha.2](https://github.com/embarklabs/embark/compare/v5.0.0-alpha.1...v5.0.0-alpha.2) (2019-12-05)
### Code Refactoring
* **@embark/library-manager:** restrict versionable packages to only solc ([2543aca](https://github.com/embarklabs/embark/commit/2543aca))
### BREAKING CHANGES
* **@embark/library-manager:** Remove support for specifying the versions of `web3` and `ipfs-api` in a
project's `embark.json`.
# [5.0.0-alpha.1](https://github.com/embarklabs/embark/compare/v5.0.0-alpha.0...v5.0.0-alpha.1) (2019-11-05)
### Bug Fixes
* **@embark/templates:** ensure boilerplate template comes with valid whisper port ([bad2207](https://github.com/embarklabs/embark/commit/bad2207)), closes [/github.com/embarklabs/embark/commit/e330b338ea2a45acb14eebd93af93bc2aba62516#diff-a61fbc84e4172487789d676437f26b5fR14](https://github.com//github.com/embarklabs/embark/commit/e330b338ea2a45acb14eebd93af93bc2aba62516/issues/diff-a61fbc84e4172487789d676437f26b5fR14)
# [5.0.0-alpha.0](https://github.com/embarklabs/embark/compare/v4.1.1...v5.0.0-alpha.0) (2019-10-28)
### Code Refactoring
* initial steps toward 5.0.0-alpha.0 ([#1856](https://github.com/embarklabs/embark/issues/1856)) ([b736ebe](https://github.com/embarklabs/embark/commit/b736ebe))
### BREAKING CHANGES
* There are more than several breaking changes, including DApp configuration for
accounts.
## [4.1.1](https://github.com/embarklabs/embark/compare/v4.1.0...v4.1.1) (2019-08-28)
**Note:** Version bump only for package embark-dapp-template-boilerplate
# [4.1.0](https://github.com/embarklabs/embark/compare/v4.1.0-beta.6...v4.1.0) (2019-08-12)
**Note:** Version bump only for package embark-dapp-template-boilerplate
# [4.1.0-beta.6](https://github.com/embarklabs/embark/compare/v4.1.0-beta.5...v4.1.0-beta.6) (2019-08-09)
### Features
* **@embark/pipeline:** add minimalContractSize to remove bytecode ([b0cccae](https://github.com/embarklabs/embark/commit/b0cccae))
* **@embark/pipeline:** enable choosing which fields to filter out ([b5c81bd](https://github.com/embarklabs/embark/commit/b5c81bd))
# [4.1.0-beta.5](https://github.com/embarklabs/embark/compare/v4.1.0-beta.4...v4.1.0-beta.5) (2019-07-10)
**Note:** Version bump only for package embark-dapp-template-boilerplate
# [4.1.0-beta.4](https://github.com/embarklabs/embark/compare/v4.1.0-beta.3...v4.1.0-beta.4) (2019-06-27)
### Bug Fixes
* **@embark/dapps:** old link updated to the latest documentation at website ([09d7428](https://github.com/embarklabs/embark/commit/09d7428))
* **templates:** fix templates because tests don't like empty files ([908aa3b](https://github.com/embarklabs/embark/commit/908aa3b))
# [4.1.0-beta.3](https://github.com/embarklabs/embark/compare/v4.1.0-beta.2...v4.1.0-beta.3) (2019-06-07)
**Note:** Version bump only for package embark-dapp-template-boilerplate
# [4.1.0-beta.2](https://github.com/embarklabs/embark/compare/v4.1.0-beta.1...v4.1.0-beta.2) (2019-05-22)
**Note:** Version bump only for package embark-dapp-template-boilerplate
# [4.1.0-beta.1](https://github.com/embarklabs/embark/compare/v4.1.0-beta.0...v4.1.0-beta.1) (2019-05-15)
**Note:** Version bump only for package embark-dapp-template-boilerplate
# [4.1.0-beta.0](https://github.com/embarklabs/embark/compare/v4.0.0...v4.1.0-beta.0) (2019-04-17)
**Note:** Version bump only for package embark-dapp-template-boilerplate
## [4.0.2](https://github.com/embarklabs/embark/compare/v4.0.1...v4.0.2) (2019-04-11)
**Note:** Version bump only for package embark-dapp-template-boilerplate
## [4.0.1](https://github.com/embarklabs/embark/compare/v4.0.0...v4.0.1) (2019-03-26)
**Note:** Version bump only for package embark-dapp-template-boilerplate
# [4.0.0](https://github.com/embarklabs/embark/compare/v4.0.0-beta.2...v4.0.0) (2019-03-18)
**Note:** Version bump only for package embark-dapp-template-boilerplate
# [4.0.0-beta.2](https://github.com/embarklabs/embark/compare/v4.0.0-beta.1...v4.0.0-beta.2) (2019-03-18)
**Note:** Version bump only for package embark-dapp-template-boilerplate
# [4.0.0-beta.1](https://github.com/embarklabs/embark/compare/v4.0.0-beta.0...v4.0.0-beta.1) (2019-03-18)
### Features
* add repository.directory field to package.json ([a9c5e1a](https://github.com/embarklabs/embark/commit/a9c5e1a))
* **@embark/pipeline:** Add `enabled` property to pipeline config ([5ea4807](https://github.com/embarklabs/embark/commit/5ea4807))
* normalize README and package.json bugs, homepage, description ([5418f16](https://github.com/embarklabs/embark/commit/5418f16))

22
LICENSE Normal file
View File

@ -0,0 +1,22 @@
The MIT License (MIT)
Copyright (c) 2015 iuri matias
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

0
app/css/.gitkeep Normal file
View File

0
app/images/.gitkeep Normal file
View File

11
app/index.html Normal file
View File

@ -0,0 +1,11 @@
<html>
<head>
<title>Embark</title>
<link rel="stylesheet" href="css/app.css">
<script src="js/app.js"></script>
</head>
<body>
<h3>Welcome to Embark!</h3>
<p>See the <a href="https://framework.embarklabs.io/docs/quick_start.html" target="_blank">Embark's documentation</a> to see what you can do with Embark!</p>
</body>
</html>

0
app/js/.gitkeep Normal file
View File

10
app/js/index.js Normal file
View File

@ -0,0 +1,10 @@
import EmbarkJS from 'Embark/EmbarkJS';
// import your contracts
// e.g if you have a contract named SimpleStorage:
//import SimpleStorage from 'Embark/contracts/SimpleStorage';
EmbarkJS.onReady((err) => {
// You can execute contract calls after the connection
});

78
config/blockchain.js Normal file
View File

@ -0,0 +1,78 @@
// This file contains only the basic configuration you need to run Embark's node
// For additional configurations, see: https://framework.embarklabs.io/docs/blockchain_configuration.html
module.exports = {
// default applies to all environments
default: {
enabled: true,
client: "geth" // Can be geth or parity (default:geth)
},
development: {
clientConfig: {
miningMode: 'dev' // Mode in which the node mines. Options: dev, auto, always, off
}
},
privatenet: {
// Accounts to use as node accounts
// The order here corresponds to the order of `web3.eth.getAccounts`, so the first one is the `defaultAccount`
// For more account configurations, see: https://framework.embarklabs.io/docs/blockchain_accounts_configuration.html
accounts: [
{
nodeAccounts: true, // Accounts use for the node
numAddresses: "1", // Number of addresses/accounts (defaults to 1)
password: "config/development/password" // Password file for the accounts
}
],
clientConfig: {
datadir: ".embark/privatenet/datadir", // Data directory for the databases and keystore
miningMode: 'auto',
genesisBlock: "config/privatenet/genesis.json" // Genesis block to initiate on first creation of a development node
}
},
privateparitynet: {
client: "parity",
genesisBlock: "config/privatenet/genesis-parity.json",
datadir: ".embark/privatenet/datadir",
miningMode: 'off'
},
externalnode: {
endpoint: "URL_OF_THE_NODE", // Endpoint of an node to connect to. Can be on localhost or on the internet
accounts: [
{
mnemonic: "YOUR_MNEMONIC",
hdpath: "m/44'/60'/0'/0/",
numAddresses: "1"
}
]
},
testnet: {
networkType: "testnet", // Can be: testnet(ropsten), rinkeby, livenet or custom, in which case, it will use the specified networkId
syncMode: "light",
accounts: [
{
nodeAccounts: true,
password: "config/testnet/password"
}
]
},
livenet: {
networkType: "livenet",
syncMode: "light",
accounts: [
{
nodeAccounts: true,
password: "config/livenet/password"
}
]
}
// you can name an environment with specific settings and then specify with
// "embark run custom_name" or "embark blockchain custom_name"
//custom_name: {
//}
};

47
config/communication.js Normal file
View File

@ -0,0 +1,47 @@
module.exports = {
// default applies to all environments
default: {
enabled: true,
provider: "whisper", // Communication provider. Currently, Embark only supports whisper
available_providers: ["whisper"], // Array of available providers
client: "geth"
},
// default environment, merges with the settings in default
// assumed to be the intended environment by `embark run`
development: {
connection: {
host: "localhost", // Host of the provider node
port: 8547, // Port of the provider node
type: "ws" // Type of connection (ws or rpc)
}
},
// merges with the settings in default
// used with "embark run privatenet"
privatenet: {
},
// merges with the settings in default
// used with "embark run testnet"
testnet: {
},
// merges with the settings in default
// used with "embark run livenet"
livenet: {
},
// you can name an environment with specific settings and then specify with
// "embark run custom_name"
//custom_name: {
//}
// Use this section when you need a specific symmetric or private keys in whisper
/*
,keys: {
symmetricKey: "your_symmetric_key",// Symmetric key for message decryption
privateKey: "your_private_key" // Private Key to be used as a signing key and for message decryption
}
*/
//}
};

51
config/contracts.js Normal file
View File

@ -0,0 +1,51 @@
module.exports = {
// default applies to all environments
default: {
// order of connections the dapp should connect to
dappConnection: [
"$EMBARK",
"$WEB3", // uses pre existing web3 object if available (e.g in Mist)
"ws://localhost:8546",
"http://localhost:8545"
],
// Automatically call `ethereum.enable` if true.
// If false, the following code must run before sending any transaction: `await EmbarkJS.enableEthereum();`
// Default value is true.
// dappAutoEnable: true,
gas: "auto",
// Strategy for the deployment of the contracts:
// - implicit will try to deploy all the contracts located inside the contracts directory
// or the directory configured for the location of the contracts. This is default one
// when not specified
// - explicit will only attempt to deploy the contracts that are explicitly specified inside the
// contracts section.
// strategy: 'implicit',
// minimalContractSize, when set to true, tells Embark to generate contract files without the heavy bytecodes
// Using filteredFields lets you customize which field you want to filter out of the contract file (requires minimalContractSize: true)
// minimalContractSize: false,
// filteredFields: [],
deploy: {
// example:
//SimpleStorage: {
// args: [ 100 ]
//}
}
},
// default environment, merges with the settings in default
// assumed to be the intended environment by `embark run`
development: {},
// merges with the settings in default
// used with "embark run privatenet"
privatenet: {},
// you can name an environment with specific settings and then specify with
// "embark run custom_name" or "embark blockchain custom_name"
// custom_name: {}
};

View File

@ -0,0 +1 @@
dev_password

39
config/namesystem.js Normal file
View File

@ -0,0 +1,39 @@
module.exports = {
// default applies to all environments
default: {
enabled: true,
available_providers: ["ens"],
provider: "ens"
},
// default environment, merges with the settings in default
// assumed to be the intended environment by `embark run`
development: {
register: {
rootDomain: "embark.eth",
subdomains: {
'status': '0x1a2f3b98e434c02363f3dac3174af93c1d690914'
}
}
},
// merges with the settings in default
// used with "embark run privatenet"
privatenet: {
},
// merges with the settings in default
// used with "embark run testnet"
testnet: {
},
// merges with the settings in default
// used with "embark run livenet"
livenet: {
},
// you can name an environment with specific settings and then specify with
// "embark run custom_name" or "embark blockchain custom_name"
//custom_name: {
//}
};

29
config/pipeline.js Normal file
View File

@ -0,0 +1,29 @@
// Embark has support for Flow enabled by default in its built-in webpack
// config: type annotations will automatically be stripped out of DApp sources
// without any additional configuration. Note that type checking is not
// performed during builds.
// To enable Flow type checking refer to the preconfigured template:
// https://github.com/embarklabs/embark-flow-template
// A new DApp can be created from that template with:
// embark new --template flow
// NOTE: the `--template` option is DEPRECATED in v5.
module.exports = {
typescript: false,
// Setting `typescript: true` in this config will disable Flow support in
// Embark's default webpack config and enable TypeScript support: .ts and
// .tsx sources will automatically be transpiled into JavaScript without any
// additional configuration. Note that type checking is not performed during
// builds.
// To enable TypeScript type checking refer to the preconfigured template:
// https://github.com/embarklabs/embark-typescript-template
// A new DApp can be created from that template with:
// embark new --template typescript
// NOTE: the `--template` option is DEPRECATED in v5.
enabled: true
// Setting `enabled: false` in this config will disable Embark's built-in Webpack
// pipeline. The developer will need to use a different frontend build tool, such as
// `create-react-app` or Angular CLI to build their dapp
};

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,20 @@
{
"config": {
"homesteadBlock": 0,
"byzantiumBlock": 0,
"eip155Block": 0,
"eip158Block": 0,
"daoForkSupport": true
},
"nonce": "0x0000000000000042",
"difficulty": "0x0",
"alloc": {
"0x3333333333333333333333333333333333333333": {"balance": "15000000000000000000"}
},
"mixhash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"coinbase": "0x3333333333333333333333333333333333333333",
"timestamp": "0x00",
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"extraData": "0x",
"gasLimit": "0x7a1200"
}

View File

@ -0,0 +1 @@
dev_password

58
config/storage.js Normal file
View File

@ -0,0 +1,58 @@
module.exports = {
// default applies to all environments
default: {
enabled: true,
ipfs_bin: "ipfs",
available_providers: ["ipfs"],
upload: {
provider: "ipfs",
host: "localhost",
port: 5001
},
dappConnection: [
{
provider: "ipfs",
host: "localhost",
port: 5001,
getUrl: "http://localhost:8080/ipfs/"
}
]
// Configuration to start Swarm in the same terminal as `embark run`
/*,account: {
address: "YOUR_ACCOUNT_ADDRESS", // Address of account accessing Swarm
password: "PATH/TO/PASSWORD/FILE" // File containing the password of the account
},
swarmPath: "PATH/TO/SWARM/EXECUTABLE" // Path to swarm executable (default: swarm)*/
},
// default environment, merges with the settings in default
// assumed to be the intended environment by `embark run`
development: {
upload: {
provider: "ipfs",
host: "localhost",
port: 5001,
getUrl: "http://localhost:8080/ipfs/"
}
},
// merges with the settings in default
// used with "embark run privatenet"
privatenet: {
},
// merges with the settings in default
// used with "embark run testnet"
testnet: {
},
// merges with the settings in default
// used with "embark run livenet"
livenet: {
},
// you can name an environment with specific settings and then specify with
// "embark run custom_name"
//custom_name: {
//}
};

1
config/testnet/password Normal file
View File

@ -0,0 +1 @@
test_password

6
config/webserver.js Normal file
View File

@ -0,0 +1,6 @@
module.exports = {
enabled: true,
host: "localhost",
openBrowser: true,
port: 8000
};

0
contracts/.gitkeep Normal file
View File

15
contracts/ERC20Token.sol Normal file
View File

@ -0,0 +1,15 @@
pragma solidity ^0.6.1;
// https://github.com/ethereum/EIPs/issues/20
interface ERC20Token {
event Transfer(address indexed _from, address indexed _to, uint256 _value);
event Approval(address indexed _owner, address indexed _spender, uint256 _value);
function transfer(address _to, uint256 _value) external returns (bool success);
function approve(address _spender, uint256 _value) external returns (bool success);
function transferFrom(address _from, address _to, uint256 _value) external returns (bool success);
function balanceOf(address _owner) external view returns (uint256 balance);
function allowance(address _owner, address _spender) external view returns (uint256 remaining);
function totalSupply() external view returns (uint256 supply);
}

146
contracts/GiftsBatch.sol Normal file
View File

@ -0,0 +1,146 @@
pragma solidity ^0.6.1;
pragma experimental ABIEncoderV2;
import "./ERC20Token.sol";
contract GiftsBatch {
address payable public owner;
ERC20Token public tokenContract;
uint256 public expirationTime;
struct Gift {
address keycard;
uint256 amount;
bytes32 code;
}
mapping(address => Gift) public gifts;
uint256 public totalSupply;
uint256 public availableSupply;
struct Redeem {
address keycard;
address receiver;
bytes32 code;
}
bytes32 constant EIP712DOMAIN_TYPEHASH = keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)");
bytes32 constant REDEEM_TYPEHASH = keccak256("Redeem(address keycard,address receiver,bytes32 code)");
bytes32 DOMAIN_SEPARATOR;
modifier onlyOwner() {
require(msg.sender == owner, "owner required");
_;
}
constructor(address _tokenAddress, uint256 _expirationTime) public {
tokenContract = ERC20Token(_tokenAddress);
expirationTime = _expirationTime;
owner = msg.sender;
DOMAIN_SEPARATOR = keccak256(abi.encode(
EIP712DOMAIN_TYPEHASH,
keccak256("KeycardGift"),
keccak256("1"),
getChainID(),
address(this)
));
}
function getChainID() internal pure returns (uint256) {
uint256 id;
assembly {
id := chainid()
}
return id;
}
function addSupply(uint256 amount) external onlyOwner {
bool success = tokenContract.transferFrom(msg.sender, address(this), amount);
assert(success);
totalSupply += amount;
availableSupply += amount;
}
function createGift(address keycard, uint256 amount, bytes32 code) external onlyOwner {
require(amount > 0, "invalid amount");
require(availableSupply >= amount, "low supply");
Gift storage gift = gifts[keycard];
require(gift.amount == 0, "keycard already used");
gift.keycard = keycard;
gift.amount = amount;
gift.code = code;
availableSupply -= amount;
}
// function redeem(address keycard, address receiver, bytes32 code, bytes calldata sig) external {
function redeem(Redeem calldata _redeem, bytes calldata sig) external {
require(block.timestamp < expirationTime, "expired gift");
bool signedByKeycard = verify(_redeem, sig);
require(signedByKeycard, "not signed by keycard");
Gift memory gift = gifts[_redeem.keycard];
require(gift.amount > 0, "not found");
bytes32 codeHash = keccak256(abi.encodePacked(_redeem.code));
require(codeHash == gift.code, "invalid code");
totalSupply -= gift.amount;
tokenContract.transfer(_redeem.receiver, gift.amount);
}
function kill() external onlyOwner {
require(block.timestamp >= expirationTime, "not expired yet");
bool success = tokenContract.transfer(owner, totalSupply);
assert(success);
selfdestruct(owner);
}
function hashRedeem(Redeem memory _redeem) internal pure returns (bytes32) {
return keccak256(abi.encode(
REDEEM_TYPEHASH,
_redeem.keycard,
_redeem.receiver,
_redeem.code
));
}
function verify(Redeem memory _redeem, bytes memory sig) internal view returns(bool) {
require(sig.length == 65, "bad signature length");
bytes32 r;
bytes32 s;
uint8 v;
assembly {
r := mload(add(sig, 32))
s := mload(add(sig, 64))
v := byte(0, mload(add(sig, 96)))
}
if (v < 27) {
v += 27;
}
require(v == 27 || v == 28, "signature version doesn't match");
bytes32 digest = keccak256(abi.encodePacked(
"\x19\x01",
DOMAIN_SEPARATOR,
hashRedeem(_redeem)
));
return ecrecover(digest, v, r, s) == _redeem.keycard;
}
}

110
contracts/StandardToken.sol Normal file
View File

@ -0,0 +1,110 @@
pragma solidity ^0.6.1;
import "./ERC20Token.sol";
contract StandardToken is ERC20Token {
uint256 private supply;
mapping (address => uint256) balances;
mapping (address => mapping (address => uint256)) allowed;
constructor() internal { }
function transfer(
address _to,
uint256 _value
)
external
override(ERC20Token)
returns (bool success)
{
return transfer(msg.sender, _to, _value);
}
function approve(address _spender, uint256 _value)
external
override(ERC20Token)
returns (bool success)
{
allowed[msg.sender][_spender] = _value;
emit Approval(msg.sender, _spender, _value);
return true;
}
function transferFrom(
address _from,
address _to,
uint256 _value
)
external
override(ERC20Token)
returns (bool success)
{
if (balances[_from] >= _value &&
allowed[_from][msg.sender] >= _value &&
_value > 0) {
allowed[_from][msg.sender] -= _value;
return transfer(_from, _to, _value);
} else {
return false;
}
}
function allowance(address _owner, address _spender)
external
view
override(ERC20Token)
returns (uint256 remaining)
{
return allowed[_owner][_spender];
}
function balanceOf(address _owner)
external
view
override(ERC20Token)
returns (uint256 balance)
{
return balances[_owner];
}
function totalSupply()
external
view
override(ERC20Token)
returns(uint256 currentTotalSupply)
{
return supply;
}
function mint(
address _to,
uint256 _amount
)
internal
{
balances[_to] += _amount;
supply += _amount;
emit Transfer(address(0x0), _to, _amount);
}
function transfer(
address _from,
address _to,
uint256 _value
)
internal
returns (bool success)
{
if (balances[_from] >= _value && _value > 0) {
balances[_from] -= _value;
balances[_to] += _value;
emit Transfer(_from, _to, _value);
return true;
} else {
return false;
}
}
}

19
contracts/TestToken.sol Normal file
View File

@ -0,0 +1,19 @@
pragma solidity ^0.6.1;
import "./StandardToken.sol";
/**
* @notice ERC20Token for test scripts, can be minted by anyone.
*/
contract TestToken is StandardToken {
constructor() public { }
/**
* @notice any caller can mint any `_amount`
* @param _amount how much to be minted
*/
function mint(uint256 _amount) public {
mint(msg.sender, _amount);
}
}

23
embark.json Normal file
View File

@ -0,0 +1,23 @@
{
"contracts": ["contracts/**"],
"app": {
"css/app.css": ["app/css/**"],
"js/app.js": ["app/js/index.js"],
"images/": ["app/images/**"],
"index.html": "app/index.html"
},
"buildDir": "dist/",
"config": "config/",
"versions": {
"solc": "0.6.1"
},
"plugins": {
},
"options": {
"solc": {
"optimize": true,
"optimize-runs": 200
}
},
"generationDir": "embarkArtifacts"
}

4944
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

19
package.json Normal file
View File

@ -0,0 +1,19 @@
{
"name": "gift-keycard",
"version": "0.0.1",
"description": "",
"scripts": {
"test": "embark test"
},
"keywords": [],
"author": "",
"license": "MIT",
"devDependencies": {
"embarkjs": "^5.1.1",
"embarkjs-ens": "^5.1.1",
"embarkjs-ipfs": "^5.1.1",
"embarkjs-swarm": "^5.1.1",
"embarkjs-web3": "^5.1.1",
"embarkjs-whisper": "^5.1.1"
}
}

307
test/contract_spec.js Normal file
View File

@ -0,0 +1,307 @@
const TestToken = require('Embark/contracts/TestToken');
const GiftsBatch = require('Embark/contracts/GiftsBatch');
let shop,
user;
// For documentation please see https://framework.embarklabs.io/docs/contracts_testing.html
config({
contracts: {
deploy: {
"TestToken": {
args: [],
}
}
}
}, (_err, _accounts) => {
shop = _accounts[0];
keycard_1 = _accounts[1];
keycard_2 = _accounts[2];
user = _accounts[3];
});
const TOTAL_SUPPLY = 10000;
const GIFT_AMOUNT = 10;
const REDEEM_CODE = web3.utils.sha3("hello world");
const NOW = Math.round(new Date().getTime() / 1000);
const EXPIRATION_TIME = NOW + 60 * 60 * 24; // in 24 hours
let sendMethod;
async function signRedeem(contractAddress, signer, message) {
const result = await web3.eth.net.getId();
const chainId = parseInt(result);
let domain = [
{ name: "name", type: "string" },
{ name: "version", type: "string" },
{ name: "chainId", type: "uint256" },
{ name: "verifyingContract", type: "address" }
];
let redeem = [
{ name: "keycard", type: "address" },
{ name: "receiver", type: "address" },
{ name: "code", type: "bytes32" },
];
let domainData = {
name: "KeycardGift",
version: "1",
chainId: chainId,
verifyingContract: contractAddress
};
let data = {
types: {
EIP712Domain: domain,
Redeem: redeem,
},
primaryType: "Redeem",
domain: domainData,
message: message
};
return new Promise((resolve, reject) => {
sendMethod({
jsonrpc: '2.0',
id: Date.now().toString().substring(9),
method: "eth_signTypedData",
params: [signer, data],
from: keycard_2
}, (error, res) => {
if (error) {
return reject(error);
}
resolve(res.result);
});
});
}
function mineAt(timestamp) {
return new Promise((resolve, reject) => {
sendMethod({
jsonrpc: '2.0',
method: "evm_mine",
params: [timestamp],
id: Date.now().toString().substring(9)
}, (error, res) => {
if (error) {
return reject(error);
}
resolve(res.result);
});
});
}
contract("TestToken", function () {
sendMethod = (web3.currentProvider.sendAsync) ? web3.currentProvider.sendAsync.bind(web3.currentProvider) : web3.currentProvider.send.bind(web3.currentProvider);
let giftsBatchInstance;
it("shop buys 100 tokens", async function () {
let supply = await TestToken.methods.totalSupply().call();
assert.equal(parseInt(supply), 0);
await TestToken.methods.mint(TOTAL_SUPPLY).send({
from: shop,
});
supply = await TestToken.methods.totalSupply().call();
assert.equal(parseInt(supply), TOTAL_SUPPLY);
let shopBalance = await TestToken.methods.balanceOf(shop).call();
assert.equal(parseInt(shopBalance), TOTAL_SUPPLY);
});
it("deploy", async function() {
const deploy = GiftsBatch.deploy({
arguments: [TestToken.address, EXPIRATION_TIME],
});
giftsBatchInstance = await deploy.send({
gas: 900000,
});
const approve = TestToken.methods.approve(giftsBatchInstance._address, TOTAL_SUPPLY)
const approveGas = await approve.estimateGas();
await approve.send({
from: shop,
gas: approveGas,
});
const addSupply = giftsBatchInstance.methods.addSupply(TOTAL_SUPPLY)
const addSupplyGas = await addSupply.estimateGas();
await addSupply.send({
from: shop,
gas: addSupplyGas,
});
let shopBalance = await TestToken.methods.balanceOf(shop).call();
assert.equal(parseInt(shopBalance), 0);
let factoryBalance = await TestToken.methods.balanceOf(giftsBatchInstance._address).call();
assert.equal(parseInt(factoryBalance), TOTAL_SUPPLY);
let totalSupply = await giftsBatchInstance.methods.totalSupply().call();
assert.equal(parseInt(totalSupply), TOTAL_SUPPLY);
let availableSupply = await giftsBatchInstance.methods.availableSupply().call();
assert.equal(parseInt(availableSupply), TOTAL_SUPPLY);
});
async function testCreateGift(keycard, amount) {
const redeemCodeHash = web3.utils.sha3(REDEEM_CODE);
const createGift = giftsBatchInstance.methods.createGift(keycard, amount, redeemCodeHash);
const createGiftGas = await createGift.estimateGas();
await createGift.send({
from: shop,
gas: createGiftGas,
});
let totalSupply = await giftsBatchInstance.methods.totalSupply().call();
assert.equal(parseInt(totalSupply), TOTAL_SUPPLY);
let availableSupply = await giftsBatchInstance.methods.availableSupply().call();
assert.equal(parseInt(availableSupply), TOTAL_SUPPLY - amount);
}
it("createGift should fail amount is zero", async function() {
try {
await testCreateGift(keycard_1, 0);
assert.fail("createGift should have failed");
} catch(e) {
assert.match(e.message, /invalid amount/);
}
});
it("createGift fails if amount > totalSupply", async function() {
try {
await testCreateGift(keycard_1, TOTAL_SUPPLY + 1);
assert.fail("createGift should have failed");
} catch(e) {
assert.match(e.message, /low supply/);
}
});
it("createGift", async function() {
await testCreateGift(keycard_1, GIFT_AMOUNT);
});
it("createGift should fail if keycard has already been used", async function() {
try {
await testCreateGift(keycard_1, 1);
assert.fail("createGift should have failed");
} catch(e) {
assert.match(e.message, /keycard already used/);
}
});
it("createGift amount > availableSupply", async function() {
try {
await testCreateGift(keycard_2, TOTAL_SUPPLY - GIFT_AMOUNT + 1);
assert.fail("createGift should have failed");
} catch(e) {
assert.match(e.message, /low supply/);
}
});
async function testRedeem(redeemCode) {
let totalSupply = await giftsBatchInstance.methods.totalSupply().call();
assert.equal(parseInt(totalSupply), TOTAL_SUPPLY);
let factoryBalance = await TestToken.methods.balanceOf(giftsBatchInstance._address).call();
assert.equal(parseInt(factoryBalance), TOTAL_SUPPLY);
let userBalance = await TestToken.methods.balanceOf(user).call();
assert.equal(parseInt(userBalance), 0);
// const gift = await giftsBatchInstance.methods.gifts(keycard_1).call();
// const giftBlockNumber = gift.blockNumber;
// const message = web3.utils.sha3(user);
// const sig = await web3.eth.sign(message, keycard_1);
const message = {
keycard: keycard_1,
receiver: user,
code: redeemCode,
};
const sig = await signRedeem(giftsBatchInstance._address, keycard_1, message);
const redeem = giftsBatchInstance.methods.redeem(message, sig);
const redeemGas = await redeem.estimateGas();
await redeem.send({
from: user,
gas: redeemGas,
});
factoryBalance = await TestToken.methods.balanceOf(giftsBatchInstance._address).call();
assert.equal(parseInt(factoryBalance), TOTAL_SUPPLY - GIFT_AMOUNT);
userBalance = await TestToken.methods.balanceOf(user).call();
assert.equal(parseInt(userBalance), GIFT_AMOUNT);
totalSupply = await giftsBatchInstance.methods.totalSupply().call();
assert.equal(parseInt(totalSupply), TOTAL_SUPPLY - GIFT_AMOUNT);
}
// it("cannot redeem after expiration date", async function() {
// await mineAt(EXPIRATION_TIME);
// try {
// await testRedeem(REDEEM_CODE);
// assert.fail("redeem should have failed");
// } catch(e) {
// assert.match(e.message, /expired/);
// }
// });
// it("cannot redeem with invalid code", async function() {
// await mineAt(NOW);
// try {
// await testRedeem(web3.utils.sha3("bad-code"));
// assert.fail("redeem should have failed");
// } catch(e) {
// assert.match(e.message, /invalid code/);
// }
// });
it("can redeem before expiration date", async function() {
await mineAt(NOW);
await testRedeem(REDEEM_CODE);
});
// async function testKill() {
// let shopBalance = await TestToken.methods.balanceOf(shop).call();
// assert.equal(parseInt(shopBalance), 0);
// let factoryBalance = await TestToken.methods.balanceOf(giftsBatchInstance._address).call();
// assert.equal(parseInt(factoryBalance), TOTAL_SUPPLY - GIFT_AMOUNT);
// await giftsBatchInstance.methods.kill().send({
// from: shop,
// });
// shopBalance = await TestToken.methods.balanceOf(shop).call();
// assert.equal(parseInt(shopBalance), TOTAL_SUPPLY - GIFT_AMOUNT);
// factoryBalance = await TestToken.methods.balanceOf(giftsBatchInstance._address).call();
// assert.equal(parseInt(factoryBalance), 0);
// }
// it("shop cannot kill contract before expirationTime", async function() {
// await mineAt(NOW);
// try {
// await testKill();
// assert.fail("redeem should have failed");
// } catch(e) {
// assert.match(e.message, /not expired yet/);
// }
// });
// it("shop can kill contract after expirationTime", async function() {
// await mineAt(EXPIRATION_TIME);
// await testKill();
// });
});