Merge pull request #17 from status-im/test/teller
add teller network to test folder
This commit is contained in:
commit
ff1c94790a
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"presets": [
|
||||
"@babel/preset-env",
|
||||
"@babel/preset-react"
|
||||
],
|
||||
"plugins": [
|
||||
"@babel/plugin-proposal-class-properties"
|
||||
]
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
SKIP_PREFLIGHT_CHECK=true
|
|
@ -0,0 +1,292 @@
|
|||
{
|
||||
"env": {
|
||||
"browser": true,
|
||||
"es6": true,
|
||||
"node": true,
|
||||
"jest": true
|
||||
},
|
||||
"parser": "babel-eslint",
|
||||
"extends": [
|
||||
"eslint:recommended",
|
||||
"plugin:react/recommended"
|
||||
],
|
||||
"parserOptions": {
|
||||
"sourceType": "module",
|
||||
"ecmaVersion": 2018,
|
||||
"ecmaFeatures": {
|
||||
"jsx": true
|
||||
}
|
||||
},
|
||||
"globals": {
|
||||
"__": true
|
||||
},
|
||||
"plugins": [
|
||||
"react"
|
||||
],
|
||||
"rules": {
|
||||
"accessor-pairs": "error",
|
||||
"array-bracket-newline": "error",
|
||||
"array-bracket-spacing": [
|
||||
"error",
|
||||
"never"
|
||||
],
|
||||
"array-callback-return": "off",
|
||||
"array-element-newline": "off",
|
||||
"arrow-body-style": "off",
|
||||
"arrow-parens": "off",
|
||||
"arrow-spacing": [
|
||||
"error",
|
||||
{
|
||||
"after": true,
|
||||
"before": true
|
||||
}
|
||||
],
|
||||
"block-scoped-var": "error",
|
||||
"block-spacing": "error",
|
||||
"brace-style": "off",
|
||||
"callback-return": "off",
|
||||
"camelcase": "off",
|
||||
"capitalized-comments": "off",
|
||||
"class-methods-use-this": "off",
|
||||
"comma-dangle": "error",
|
||||
"comma-spacing": "off",
|
||||
"comma-style": [
|
||||
"error",
|
||||
"last"
|
||||
],
|
||||
"complexity": "error",
|
||||
"computed-property-spacing": [
|
||||
"error",
|
||||
"never"
|
||||
],
|
||||
"consistent-return": "off",
|
||||
"consistent-this": "off",
|
||||
"curly": "off",
|
||||
"default-case": "error",
|
||||
"dot-location": [
|
||||
"error",
|
||||
"property"
|
||||
],
|
||||
"dot-notation": "off",
|
||||
"eol-last": "error",
|
||||
"eqeqeq": "error",
|
||||
"for-direction": "error",
|
||||
"func-call-spacing": "error",
|
||||
"func-name-matching": "error",
|
||||
"func-names": "off",
|
||||
"func-style": "off",
|
||||
"function-paren-newline": "off",
|
||||
"generator-star-spacing": "error",
|
||||
"getter-return": "error",
|
||||
"global-require": "off",
|
||||
"guard-for-in": "off",
|
||||
"handle-callback-err": "off",
|
||||
"id-blacklist": "error",
|
||||
"id-length": "off",
|
||||
"id-match": "error",
|
||||
"indent": "off",
|
||||
"indent-legacy": "off",
|
||||
"init-declarations": "off",
|
||||
"jsx-quotes": "error",
|
||||
"key-spacing": "off",
|
||||
"keyword-spacing": "off",
|
||||
"line-comment-position": "off",
|
||||
"lines-around-comment": "error",
|
||||
"lines-around-directive": "error",
|
||||
"max-depth": "error",
|
||||
"max-len": "off",
|
||||
"max-lines": "off",
|
||||
"max-params": "off",
|
||||
"max-statements": "off",
|
||||
"max-statements-per-line": "off",
|
||||
"multiline-ternary": [
|
||||
"error",
|
||||
"never"
|
||||
],
|
||||
"new-parens": "off",
|
||||
"newline-after-var": "off",
|
||||
"newline-before-return": "off",
|
||||
"newline-per-chained-call": "off",
|
||||
"no-alert": "error",
|
||||
"no-array-constructor": "error",
|
||||
"no-await-in-loop": "error",
|
||||
"no-bitwise": "error",
|
||||
"no-buffer-constructor": "error",
|
||||
"no-caller": "error",
|
||||
"no-catch-shadow": "error",
|
||||
"no-confusing-arrow": "error",
|
||||
"no-console": "off",
|
||||
"no-continue": "off",
|
||||
"no-debugger": "warn",
|
||||
"no-div-regex": "error",
|
||||
"no-duplicate-imports": "error",
|
||||
"no-else-return": 2,
|
||||
"no-empty-function": "off",
|
||||
"no-eq-null": "error",
|
||||
"no-eval": "off",
|
||||
"no-extend-native": "error",
|
||||
"no-extra-bind": "error",
|
||||
"no-extra-label": "error",
|
||||
"no-extra-parens": "off",
|
||||
"no-floating-decimal": "error",
|
||||
"no-implicit-coercion": [
|
||||
"error",
|
||||
{
|
||||
"allow": ["!!"]
|
||||
}
|
||||
],
|
||||
"no-implicit-globals": "error",
|
||||
"no-implied-eval": "error",
|
||||
"no-inline-comments": "off",
|
||||
"no-inner-declarations": [
|
||||
"error",
|
||||
"functions"
|
||||
],
|
||||
"no-invalid-this": "off",
|
||||
"no-iterator": "error",
|
||||
"no-label-var": "error",
|
||||
"no-labels": "error",
|
||||
"no-lone-blocks": "error",
|
||||
"no-lonely-if": "off",
|
||||
"no-loop-func": "off",
|
||||
"no-magic-numbers": "off",
|
||||
"no-mixed-operators": "error",
|
||||
"no-mixed-requires": "error",
|
||||
"no-multi-assign": "error",
|
||||
"no-multi-spaces": "off",
|
||||
"no-multi-str": "error",
|
||||
"no-multiple-empty-lines": "error",
|
||||
"no-native-reassign": "error",
|
||||
"no-negated-condition": "off",
|
||||
"no-negated-in-lhs": "error",
|
||||
"no-nested-ternary": "error",
|
||||
"no-new": "error",
|
||||
"no-new-func": "error",
|
||||
"no-new-object": "error",
|
||||
"no-new-require": "error",
|
||||
"no-new-wrappers": "error",
|
||||
"no-octal-escape": "error",
|
||||
"no-param-reassign": "off",
|
||||
"no-path-concat": "error",
|
||||
"no-plusplus": "off",
|
||||
"no-process-env": "off",
|
||||
"no-process-exit": "off",
|
||||
"no-proto": "error",
|
||||
"no-prototype-builtins": "off",
|
||||
"no-restricted-globals": "error",
|
||||
"no-restricted-imports": "error",
|
||||
"no-restricted-modules": "error",
|
||||
"no-restricted-properties": "error",
|
||||
"no-restricted-syntax": "error",
|
||||
"no-return-assign": "error",
|
||||
"no-return-await": "error",
|
||||
"no-script-url": "error",
|
||||
"no-self-compare": "error",
|
||||
"no-sequences": "error",
|
||||
"no-shadow": "off",
|
||||
"no-shadow-restricted-names": "error",
|
||||
"no-spaced-func": "error",
|
||||
"no-sync": "off",
|
||||
"no-tabs": "error",
|
||||
"no-template-curly-in-string": "error",
|
||||
"no-ternary": "off",
|
||||
"no-throw-literal": "error",
|
||||
"no-trailing-spaces": "off",
|
||||
"no-undef-init": "error",
|
||||
"no-undefined": "off",
|
||||
"no-underscore-dangle": "off",
|
||||
"no-unmodified-loop-condition": "error",
|
||||
"no-unneeded-ternary": "error",
|
||||
"no-unused-expressions": "error",
|
||||
"no-unused-vars": ["error", {
|
||||
"argsIgnorePattern": "^_",
|
||||
"varsIgnorePattern": "^_"
|
||||
}],
|
||||
"no-use-before-define": "off",
|
||||
"no-useless-call": "off",
|
||||
"no-useless-computed-key": "error",
|
||||
"no-useless-concat": "error",
|
||||
"no-useless-constructor": "error",
|
||||
"no-useless-escape": "off",
|
||||
"no-useless-rename": "error",
|
||||
"no-useless-return": "off",
|
||||
"no-var": "off",
|
||||
"no-void": "error",
|
||||
"no-warning-comments": "off",
|
||||
"no-whitespace-before-property": "error",
|
||||
"no-with": "error",
|
||||
"nonblock-statement-body-position": "error",
|
||||
"object-curly-newline": "off",
|
||||
"object-curly-spacing": [
|
||||
"off"
|
||||
],
|
||||
"object-property-newline": "off",
|
||||
"object-shorthand": "off",
|
||||
"one-var": "off",
|
||||
"one-var-declaration-per-line": "off",
|
||||
"operator-assignment": "off",
|
||||
"operator-linebreak": "error",
|
||||
"padded-blocks": "off",
|
||||
"padding-line-between-statements": "error",
|
||||
"prefer-arrow-callback": "off",
|
||||
"prefer-const": "off",
|
||||
"prefer-destructuring": "off",
|
||||
"prefer-numeric-literals": "error",
|
||||
"prefer-promise-reject-errors": "error",
|
||||
"prefer-reflect": "off",
|
||||
"prefer-rest-params": "off",
|
||||
"prefer-spread": "off",
|
||||
"prefer-template": "off",
|
||||
"quote-props": "off",
|
||||
"quotes": "off",
|
||||
"radix": "error",
|
||||
"require-await": "off",
|
||||
"require-jsdoc": "off",
|
||||
"rest-spread-spacing": "error",
|
||||
"semi": "error",
|
||||
"semi-spacing": [
|
||||
"error",
|
||||
{
|
||||
"after": true,
|
||||
"before": false
|
||||
}
|
||||
],
|
||||
"semi-style": [
|
||||
"error",
|
||||
"last"
|
||||
],
|
||||
"sort-imports": "off",
|
||||
"sort-keys": "off",
|
||||
"sort-vars": "off",
|
||||
"space-before-blocks": "off",
|
||||
"space-before-function-paren": "off",
|
||||
"space-in-parens": [
|
||||
"error",
|
||||
"never"
|
||||
],
|
||||
"space-infix-ops": "off",
|
||||
"space-unary-ops": "error",
|
||||
"spaced-comment": "off",
|
||||
"strict": "error",
|
||||
"switch-colon-spacing": "error",
|
||||
"symbol-description": "error",
|
||||
"template-curly-spacing": [
|
||||
"error",
|
||||
"never"
|
||||
],
|
||||
"template-tag-spacing": "error",
|
||||
"unicode-bom": [
|
||||
"error",
|
||||
"never"
|
||||
],
|
||||
"valid-jsdoc": "error",
|
||||
"vars-on-top": "off",
|
||||
"wrap-iife": "error",
|
||||
"wrap-regex": "error",
|
||||
"yield-star-spacing": "error",
|
||||
"yoda": [
|
||||
"error",
|
||||
"never"
|
||||
]
|
||||
}
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
*.sol diff linguist-language=Solidity
|
|
@ -0,0 +1,38 @@
|
|||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||
|
||||
# dependencies
|
||||
/.pnp
|
||||
.pnp.js
|
||||
|
||||
# testing
|
||||
/coverage
|
||||
|
||||
# production
|
||||
/build
|
||||
|
||||
# misc
|
||||
.DS_Store
|
||||
.env.local
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
.idea
|
||||
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
|
||||
.embark
|
||||
chains.json
|
||||
embarkConfig/production/password
|
||||
embarkConfig/livenet/password
|
||||
coverage
|
||||
dist
|
||||
node_modules
|
||||
src/embarkArtifacts
|
||||
.idea
|
||||
yarn-error.log
|
||||
cypress/videos/*
|
||||
.secret.json
|
||||
|
||||
crytic-export/
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"extends": "solhint:default"
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
node_modules
|
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"extends": "solium:all",
|
||||
"plugins": [
|
||||
"security"
|
||||
],
|
||||
"rules": {
|
||||
"quotes": [
|
||||
"error",
|
||||
"double"
|
||||
],
|
||||
"indentation": [
|
||||
"error",
|
||||
4
|
||||
],
|
||||
"arg-overflow": [
|
||||
"warning",
|
||||
3
|
||||
]
|
||||
}
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
pragma solidity >=0.5.0 <0.6.0;
|
||||
|
||||
contract Controlled {
|
||||
/// @notice The address of the controller is the only address that can call
|
||||
/// a function with this modifier
|
||||
modifier onlyController {
|
||||
require(msg.sender == controller, "Unauthorized");
|
||||
_;
|
||||
}
|
||||
|
||||
address payable public controller;
|
||||
|
||||
constructor() internal {
|
||||
controller = msg.sender;
|
||||
}
|
||||
|
||||
/// @notice Changes the controller of the contract
|
||||
/// @param _newController The new controller of the contract
|
||||
function changeController(address payable _newController) public onlyController {
|
||||
controller = _newController;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,67 @@
|
|||
/* solium-disable no-empty-blocks */
|
||||
/* solium-disable security/no-inline-assembly */
|
||||
|
||||
pragma solidity >=0.5.0 <0.6.0;
|
||||
|
||||
/**
|
||||
* @dev Uses ethereum signed messages
|
||||
*/
|
||||
contract MessageSigned {
|
||||
|
||||
constructor() internal {}
|
||||
|
||||
/**
|
||||
* @dev recovers address who signed the message
|
||||
* @param _signHash operation ethereum signed message hash
|
||||
* @param _messageSignature message `_signHash` signature
|
||||
*/
|
||||
function _recoverAddress(bytes32 _signHash, bytes memory _messageSignature)
|
||||
internal
|
||||
pure
|
||||
returns(address)
|
||||
{
|
||||
uint8 v;
|
||||
bytes32 r;
|
||||
bytes32 s;
|
||||
(v,r,s) = signatureSplit(_messageSignature);
|
||||
return ecrecover(_signHash, v, r, s);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Hash a hash with `"\x19Ethereum Signed Message:\n32"`
|
||||
* @param _hash Sign to hash.
|
||||
* @return Hash to be signed.
|
||||
*/
|
||||
function _getSignHash(bytes32 _hash) internal pure returns (bytes32 signHash) {
|
||||
signHash = keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", _hash));
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev divides bytes signature into `uint8 v, bytes32 r, bytes32 s`
|
||||
* @param _signature Signature string
|
||||
*/
|
||||
function signatureSplit(bytes memory _signature)
|
||||
internal
|
||||
pure
|
||||
returns (uint8 v, bytes32 r, bytes32 s)
|
||||
{
|
||||
require(_signature.length == 65, "Bad signature length");
|
||||
// The signature format is a compact form of:
|
||||
// {bytes32 r}{bytes32 s}{uint8 v}
|
||||
// Compact means, uint8 is not padded to 32 bytes.
|
||||
assembly {
|
||||
r := mload(add(_signature, 32))
|
||||
s := mload(add(_signature, 64))
|
||||
// Here we are loading the last 32 bytes, including 31 bytes
|
||||
// of 's'. There is no 'mload8' to do this.
|
||||
//
|
||||
// 'byte' is not working due to the Solidity parser, so lets
|
||||
// use the second best option, 'and'
|
||||
v := and(mload(add(_signature, 65)), 0xff)
|
||||
}
|
||||
if (v < 27) {
|
||||
v += 27;
|
||||
}
|
||||
require(v == 27 || v == 28, "Bad signature version");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,82 @@
|
|||
pragma solidity >=0.5.0 <0.6.0;
|
||||
|
||||
/**
|
||||
* @title Ownable
|
||||
* @dev The Ownable contract has an owner address, and provides basic authorization control
|
||||
* functions, this simplifies the implementation of "user permissions".
|
||||
*/
|
||||
contract Ownable {
|
||||
address private _owner;
|
||||
|
||||
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
|
||||
|
||||
/**
|
||||
* @dev The Ownable constructor sets the original `owner` of the contract to the sender
|
||||
* account.
|
||||
*/
|
||||
constructor () internal {
|
||||
_owner = msg.sender;
|
||||
emit OwnershipTransferred(address(0), _owner);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Get the contract's owner
|
||||
* @return the address of the owner.
|
||||
*/
|
||||
function owner() public view returns (address) {
|
||||
return _owner;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Throws if called by any account other than the owner.
|
||||
*/
|
||||
modifier onlyOwner() {
|
||||
require(isOwner(), "Only the contract's owner can invoke this function");
|
||||
_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Sets an owner address
|
||||
* @param _newOwner new owner address
|
||||
*/
|
||||
function _setOwner(address _newOwner) internal {
|
||||
_owner = _newOwner;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev is sender the owner of the contract?
|
||||
* @return true if `msg.sender` is the owner of the contract.
|
||||
*/
|
||||
function isOwner() public view returns (bool) {
|
||||
return msg.sender == _owner;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Allows the current owner to relinquish control of the contract.
|
||||
* Renouncing to ownership will leave the contract without an owner.
|
||||
* It will not be possible to call the functions with the `onlyOwner`
|
||||
* modifier anymore.
|
||||
*/
|
||||
function renounceOwnership() external onlyOwner {
|
||||
emit OwnershipTransferred(_owner, address(0));
|
||||
_owner = address(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Allows the current owner to transfer control of the contract to a newOwner.
|
||||
* @param _newOwner The address to transfer ownership to.
|
||||
*/
|
||||
function transferOwnership(address _newOwner) external onlyOwner {
|
||||
_transferOwnership(_newOwner);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Transfers control of the contract to a newOwner.
|
||||
* @param _newOwner The address to transfer ownership to.
|
||||
*/
|
||||
function _transferOwnership(address _newOwner) internal {
|
||||
require(_newOwner != address(0), "New owner cannot be address(0)");
|
||||
emit OwnershipTransferred(_owner, _newOwner);
|
||||
_owner = _newOwner;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
pragma solidity >=0.5.0 <0.6.0;
|
||||
|
||||
import "./Ownable.sol";
|
||||
|
||||
/**
|
||||
* @title Pausable
|
||||
* @dev Makes contract functions pausable by the owner
|
||||
*/
|
||||
contract Pausable is Ownable {
|
||||
|
||||
event Paused();
|
||||
event Unpaused();
|
||||
|
||||
bool public paused;
|
||||
|
||||
constructor () internal {
|
||||
paused = false;
|
||||
}
|
||||
|
||||
modifier whenNotPaused() {
|
||||
require(!paused, "Contract must be unpaused");
|
||||
_;
|
||||
}
|
||||
|
||||
modifier whenPaused() {
|
||||
require(paused, "Contract must be paused");
|
||||
_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Disables contract functions marked with "whenNotPaused" and enables the use of functions marked with "whenPaused"
|
||||
* Only the owner of the contract can invoke this function
|
||||
*/
|
||||
function pause() external onlyOwner whenNotPaused {
|
||||
paused = true;
|
||||
emit Paused();
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Enables contract functions marked with "whenNotPaused" and disables the use of functions marked with "whenPaused"
|
||||
* Only the owner of the contract can invoke this function
|
||||
*/
|
||||
function unpause() external onlyOwner whenPaused {
|
||||
paused = false;
|
||||
emit Unpaused();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,140 @@
|
|||
/* solium-disable security/no-block-members */
|
||||
pragma solidity >=0.5.0 <0.6.0;
|
||||
|
||||
import "../common/Ownable.sol";
|
||||
import "../token/ERC20Token.sol";
|
||||
import "./KyberNetworkProxy.sol";
|
||||
|
||||
/**
|
||||
* @title KyberFeeBurner
|
||||
* @dev Contract that holds assets for the purpose of trading them to SNT and burning them
|
||||
* @dev Assets come from the Escrow contract fees
|
||||
*/
|
||||
contract KyberFeeBurner is Ownable {
|
||||
|
||||
address public SNT;
|
||||
address public burnAddress;
|
||||
address public walletId;
|
||||
KyberNetworkProxy public kyberNetworkProxy;
|
||||
|
||||
// In Kyber's contracts, this is the address for ETH
|
||||
address constant internal ETH_TOKEN_ADDRESS = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
|
||||
|
||||
/**
|
||||
* @param _snt Address of the SNT contract
|
||||
* @param _burnAddress Address where to burn the assets
|
||||
* @param _kyberNetworkProxy License contract instance address for arbitrators
|
||||
* @param _walletId Wallet address to send part of the fees to (used for the fee sharing program)
|
||||
*/
|
||||
constructor(address _snt, address _burnAddress, address _kyberNetworkProxy, address _walletId) public {
|
||||
SNT = _snt;
|
||||
burnAddress = _burnAddress;
|
||||
kyberNetworkProxy = KyberNetworkProxy(_kyberNetworkProxy);
|
||||
walletId = _walletId;
|
||||
}
|
||||
|
||||
event SNTAddressChanged(address sender, address prevSNTAddress, address newSNTAddress);
|
||||
|
||||
/**
|
||||
* @dev Changes the SNT contract address
|
||||
* @param _snt New SNT contract address
|
||||
*/
|
||||
function setSNT(address _snt) external onlyOwner {
|
||||
emit SNTAddressChanged(msg.sender, SNT, _snt);
|
||||
SNT = _snt;
|
||||
}
|
||||
|
||||
event BurnAddressChanged(address sender, address prevBurnAddress, address newBurnAddress);
|
||||
|
||||
/**
|
||||
* @dev Changes the burn address
|
||||
* @param _burnAddress New burn address
|
||||
*/
|
||||
function setBurnAddress(address _burnAddress) external onlyOwner {
|
||||
emit BurnAddressChanged(msg.sender, burnAddress, _burnAddress);
|
||||
burnAddress = _burnAddress;
|
||||
}
|
||||
|
||||
event KyberNetworkProxyAddressChanged(address sender, address prevKyberAddress, address newKyberAddress);
|
||||
|
||||
/**
|
||||
* @dev Changes the KyberNetworkProxy contract address
|
||||
* @param _kyberNetworkProxy New KyberNetworkProxy address
|
||||
*/
|
||||
function setKyberNetworkProxyAddress(address _kyberNetworkProxy) external onlyOwner {
|
||||
emit KyberNetworkProxyAddressChanged(msg.sender, address(kyberNetworkProxy), _kyberNetworkProxy);
|
||||
kyberNetworkProxy = KyberNetworkProxy(_kyberNetworkProxy);
|
||||
}
|
||||
|
||||
event WalletIdChanged(address sender, address prevWalletId, address newWalletId);
|
||||
|
||||
/**
|
||||
* @dev Changes the walletId address (for the fee sharing program)
|
||||
* @param _walletId New walletId address
|
||||
*/
|
||||
function setWalletId(address _walletId) external onlyOwner {
|
||||
emit WalletIdChanged(msg.sender, walletId, _walletId);
|
||||
walletId = _walletId;
|
||||
}
|
||||
|
||||
event Swap(address sender, address srcToken, address destToken, uint srcAmount, uint destAmount);
|
||||
|
||||
/**
|
||||
* @dev Swaps the selected asset to SNT and transfers it to the burn address
|
||||
* @param _token Address of the asset to trade
|
||||
*/
|
||||
function swap(address _token) external {
|
||||
uint balance = 0;
|
||||
uint minConversionRate = 0;
|
||||
uint destAmount = 0;
|
||||
|
||||
if (_token == address(0)) {
|
||||
balance = address(this).balance;
|
||||
(minConversionRate,) = kyberNetworkProxy.getExpectedRate(ETH_TOKEN_ADDRESS, SNT, balance);
|
||||
destAmount = kyberNetworkProxy.trade.value(balance)(ETH_TOKEN_ADDRESS, balance, SNT, burnAddress, 0 - uint256(1), minConversionRate, walletId);
|
||||
emit Swap(msg.sender, ETH_TOKEN_ADDRESS, SNT, balance, destAmount);
|
||||
} else {
|
||||
ERC20Token t = ERC20Token(_token);
|
||||
balance = t.balanceOf(address(this));
|
||||
if (_token == SNT) {
|
||||
require(t.transfer(burnAddress, balance), "SNT transfer failure");
|
||||
emit Swap(msg.sender, SNT, SNT, balance, balance);
|
||||
return;
|
||||
} else {
|
||||
// Mitigate ERC20 Approve front-running attack, by initially setting allowance to 0
|
||||
require(ERC20Token(_token).approve(address(kyberNetworkProxy), 0), "Failed to reset approval");
|
||||
|
||||
// Set the spender's token allowance to tokenQty
|
||||
require(ERC20Token(_token).approve(address(kyberNetworkProxy), balance), "Failed to approve trade amount");
|
||||
|
||||
(minConversionRate,) = kyberNetworkProxy.getExpectedRate(_token, SNT, balance);
|
||||
destAmount = kyberNetworkProxy.trade(_token, balance, SNT, burnAddress, 0 - uint256(1), minConversionRate, walletId);
|
||||
|
||||
emit Swap(msg.sender, _token, SNT, balance, destAmount);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
event EscapeTriggered(address sender, address token, uint amount);
|
||||
|
||||
/**
|
||||
* @dev Exits the selected asset to the owner
|
||||
* @param _token Address of the asset to exit
|
||||
*/
|
||||
function escape(address _token) external onlyOwner {
|
||||
if (_token == address(0)) {
|
||||
uint ethBalance = address(this).balance;
|
||||
address(uint160(owner())).transfer(ethBalance);
|
||||
emit EscapeTriggered(msg.sender, _token, ethBalance);
|
||||
} else {
|
||||
ERC20Token t = ERC20Token(_token);
|
||||
uint tokenBalance = t.balanceOf(address(this));
|
||||
require(t.transfer(owner(), tokenBalance), "Token transfer error");
|
||||
emit EscapeTriggered(msg.sender, _token, tokenBalance);
|
||||
}
|
||||
}
|
||||
|
||||
function() payable external {
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,67 @@
|
|||
pragma solidity >=0.5.0 <0.6.0;
|
||||
/**
|
||||
* @title KyberNetworkProxy
|
||||
* @dev Mock of the KyberNetworkProxy. Only used in development
|
||||
*/
|
||||
contract KyberNetworkProxy {
|
||||
|
||||
constructor() public {
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Get a mocked up rate for the trade
|
||||
*/
|
||||
function getExpectedRate(
|
||||
address /* src */,
|
||||
address /* dest */,
|
||||
uint /* srcQty */
|
||||
)
|
||||
public pure
|
||||
returns(uint expectedRate, uint slippageRate)
|
||||
{
|
||||
return (32749000000000000000, 31766530000000000000);
|
||||
}
|
||||
|
||||
/// @notice use token address ETH_TOKEN_ADDRESS for ether
|
||||
/// @dev makes a trade between src and dest token and send dest token to destAddress
|
||||
/// @param maxDestAmount A limit on the amount of dest tokens
|
||||
/// @return amount of actual dest tokens
|
||||
function trade(
|
||||
address /* src */,
|
||||
uint /* srcAmount */,
|
||||
address /* dest */,
|
||||
address /* destAddress */,
|
||||
uint maxDestAmount,
|
||||
uint /* minConversionRate */,
|
||||
address /* walletId */
|
||||
)
|
||||
public
|
||||
payable
|
||||
returns(uint)
|
||||
{
|
||||
return maxDestAmount;
|
||||
}
|
||||
|
||||
/// @dev makes a trade between src and dest token and send dest tokens to msg sender
|
||||
/// @return amount of actual dest tokens
|
||||
function swapTokenToToken(
|
||||
address /* src */,
|
||||
uint /* srcAmount */,
|
||||
address /* dest */,
|
||||
uint /* minConversionRate */
|
||||
)
|
||||
public pure
|
||||
returns(uint)
|
||||
{
|
||||
return 100;
|
||||
}
|
||||
|
||||
/// @dev makes a trade from Ether to token. Sends token to msg sender
|
||||
/// @return amount of actual dest tokens
|
||||
function swapEtherToToken(
|
||||
address /* token */,
|
||||
uint /* minConversionRate */
|
||||
) public payable returns(uint) {
|
||||
return 200;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,94 @@
|
|||
/* solium-disable security/no-low-level-calls */
|
||||
/* solium-disable security/no-inline-assembly */
|
||||
|
||||
pragma solidity >=0.5.0 <0.6.0;
|
||||
|
||||
// Adapted from
|
||||
// https://github.com/zeppelinos/labs/tree/master/upgradeability_using_unstructured_storage
|
||||
|
||||
import './UpgradeabilityProxy.sol';
|
||||
|
||||
/**
|
||||
* @title OwnedUpgradeabilityProxy
|
||||
* @dev This contract combines an upgradeability proxy with basic authorization control functionalities
|
||||
*/
|
||||
contract OwnedUpgradeabilityProxy is UpgradeabilityProxy {
|
||||
|
||||
/**
|
||||
* @dev Event to show ownership has been transferred
|
||||
* @param previousOwner representing the address of the previous owner
|
||||
* @param newOwner representing the address of the new owner
|
||||
*/
|
||||
event ProxyOwnershipTransferred(address previousOwner, address newOwner);
|
||||
|
||||
// Storage position of the owner of the contract
|
||||
bytes32 private constant proxyOwnerPosition = keccak256("im.status.proxy.owner");
|
||||
|
||||
/**
|
||||
* @dev the constructor sets the original owner of the contract to the sender account.
|
||||
*/
|
||||
constructor() public {
|
||||
setUpgradeabilityOwner(msg.sender);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Throws if called by any account other than the owner.
|
||||
*/
|
||||
modifier onlyProxyOwner() {
|
||||
require(msg.sender == proxyOwner(), "Only the proxy owner can invoke this function");
|
||||
_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Tells the address of the owner
|
||||
* @return the address of the owner
|
||||
*/
|
||||
function proxyOwner() public view returns (address owner) {
|
||||
bytes32 position = proxyOwnerPosition;
|
||||
assembly {
|
||||
owner := sload(position)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Sets the address of the owner
|
||||
* @param _newProxyOwner New proxy owner address
|
||||
*/
|
||||
function setUpgradeabilityOwner(address _newProxyOwner) internal {
|
||||
bytes32 position = proxyOwnerPosition;
|
||||
assembly {
|
||||
sstore(position, _newProxyOwner)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Allows the current owner to transfer control of the contract to a new Owner.
|
||||
* @param _newProxyOwner The address to transfer ownership to.
|
||||
*/
|
||||
function transferProxyOwnership(address _newProxyOwner) external onlyProxyOwner {
|
||||
require(_newProxyOwner != address(0), "Invalid new owner");
|
||||
emit ProxyOwnershipTransferred(proxyOwner(), _newProxyOwner);
|
||||
setUpgradeabilityOwner(_newProxyOwner);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Allows the proxy owner to upgrade the current version of the proxy.
|
||||
* @param _implementation representing the address of the new implementation to be set.
|
||||
*/
|
||||
function upgradeTo(address _implementation) external onlyProxyOwner {
|
||||
_upgradeTo(_implementation);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Allows the proxy owner to upgrade the current version of the proxy and call the new implementation
|
||||
* to initialize whatever is needed through a low level call.
|
||||
* @param _implementation representing the address of the new implementation to be set.
|
||||
* @param _data represents the msg.data to bet sent in the low level call. This parameter may include the function
|
||||
* signature of the implementation to be called with the needed payload
|
||||
*/
|
||||
function upgradeToAndCall(address _implementation, bytes calldata _data) external payable onlyProxyOwner {
|
||||
_upgradeTo(_implementation);
|
||||
(bool success,) = _implementation.delegatecall(_data);
|
||||
require(success, "Upgrade failed");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
/* solium-disable security/no-block-members */
|
||||
/* solium-disable security/no-inline-assembly */
|
||||
|
||||
pragma solidity >=0.5.0 <0.6.0;
|
||||
|
||||
// Adapted from
|
||||
// https://github.com/zeppelinos/labs/tree/master/upgradeability_using_unstructured_storage
|
||||
|
||||
/**
|
||||
* @title Proxy
|
||||
* @dev Basic EIP897 proxy functionality
|
||||
*/
|
||||
contract Proxy {
|
||||
|
||||
/**
|
||||
* @dev Tells the address of the implementation where every call will be delegated.
|
||||
* @return address of the implementation to which it will be delegated
|
||||
*/
|
||||
function implementation() public view returns (address);
|
||||
|
||||
/**
|
||||
* @dev Fallback function allowing to perform a delegatecall to the given implementation.
|
||||
* This function will return whatever the implementation call returns
|
||||
*/
|
||||
function () external payable {
|
||||
address _impl = implementation();
|
||||
require(_impl != address(0), "No implementation available");
|
||||
|
||||
assembly {
|
||||
let ptr := mload(0x40)
|
||||
calldatacopy(ptr, 0, calldatasize)
|
||||
let result := delegatecall(gas, _impl, ptr, calldatasize, 0, 0)
|
||||
let size := returndatasize
|
||||
returndatacopy(ptr, 0, size)
|
||||
|
||||
switch result
|
||||
case 0 { revert(ptr, size) }
|
||||
default { return(ptr, size) }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev EIP897 proxy type
|
||||
* @return 1 to indicate not upgradable proxy
|
||||
*/
|
||||
function proxyType() public pure returns (uint256) {
|
||||
return 1;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,64 @@
|
|||
/* solium-disable security/no-inline-assembly */
|
||||
|
||||
pragma solidity >=0.5.0 <0.6.0;
|
||||
|
||||
// Adapted from
|
||||
// https://github.com/zeppelinos/labs/tree/master/upgradeability_using_unstructured_storage
|
||||
|
||||
import './Proxy.sol';
|
||||
|
||||
/**
|
||||
* @title UpgradeabilityProxy
|
||||
* @dev This contract represents a proxy where the implementation address to which it will delegate can be upgraded
|
||||
*/
|
||||
contract UpgradeabilityProxy is Proxy {
|
||||
|
||||
/**
|
||||
* @dev This event will be emitted every time the implementation gets upgraded
|
||||
* @param implementation representing the address of the upgraded implementation
|
||||
*/
|
||||
event Upgraded(address indexed implementation);
|
||||
|
||||
// Storage position of the address of the current implementation
|
||||
bytes32 private constant implementationPosition = keccak256("im.status.proxy.implementation");
|
||||
|
||||
/**
|
||||
* @dev Tells the address of the current implementation
|
||||
* @return address of the current implementation
|
||||
*/
|
||||
function implementation() public view returns (address impl) {
|
||||
bytes32 position = implementationPosition;
|
||||
assembly {
|
||||
impl := sload(position)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Sets the address of the current implementation
|
||||
* @param _implementation address representing the new implementation to be set
|
||||
*/
|
||||
function _setImplementation(address _implementation) internal {
|
||||
bytes32 position = implementationPosition;
|
||||
assembly {
|
||||
sstore(position, _implementation)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Upgrades the implementation address
|
||||
* @param _implementation representing the address of the new implementation to be set
|
||||
*/
|
||||
function _upgradeTo(address _implementation) internal {
|
||||
require(implementation() != _implementation, "Current and new implementation should be different");
|
||||
_setImplementation(_implementation);
|
||||
emit Upgraded(_implementation);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev EIP897 proxy type
|
||||
* @return 2 to indicate upgradable proxy
|
||||
*/
|
||||
function proxyType() public pure returns (uint256) {
|
||||
return 2;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,136 @@
|
|||
/* solium-disable security/no-block-members */
|
||||
pragma solidity >=0.5.0 <0.6.0;
|
||||
|
||||
import "./ArbitrationLicense.sol";
|
||||
|
||||
/**
|
||||
* Arbitrable
|
||||
* @dev Utils for management of disputes
|
||||
*/
|
||||
contract Arbitrable {
|
||||
|
||||
enum ArbitrationResult {UNSOLVED, BUYER, SELLER}
|
||||
|
||||
ArbitrationLicense public arbitratorLicenses;
|
||||
|
||||
mapping(uint => ArbitrationCase) public arbitrationCases;
|
||||
|
||||
struct ArbitrationCase {
|
||||
bool open;
|
||||
address openBy;
|
||||
address arbitrator;
|
||||
ArbitrationResult result;
|
||||
string motive;
|
||||
}
|
||||
|
||||
event ArbitratorChanged(address arbitrator);
|
||||
event ArbitrationCanceled(uint escrowId);
|
||||
event ArbitrationRequired(uint escrowId);
|
||||
event ArbitrationResolved(uint escrowId, ArbitrationResult result, address arbitrator);
|
||||
|
||||
/**
|
||||
* @param _arbitratorLicenses Address of the Arbitrator Licenses contract
|
||||
*/
|
||||
constructor(address _arbitratorLicenses) public {
|
||||
arbitratorLicenses = ArbitrationLicense(_arbitratorLicenses);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param _escrowId Id of the escrow with an open dispute
|
||||
* @param _releaseFunds Release funds to the buyer
|
||||
* @param _arbitrator Address of the arbitrator solving the dispute
|
||||
* @dev Abstract contract used to perform actions after a dispute has been settled
|
||||
*/
|
||||
function _solveDispute(uint _escrowId, bool _releaseFunds, address _arbitrator) internal;
|
||||
|
||||
/**
|
||||
* @notice Get arbitrator of an escrow
|
||||
* @return address Arbitrator address
|
||||
*/
|
||||
function _getArbitrator(uint _escrowId) internal view returns(address);
|
||||
|
||||
/**
|
||||
* @notice Determine if a dispute exists/existed for an escrow
|
||||
* @param _escrowId Escrow to verify
|
||||
* @return bool result
|
||||
*/
|
||||
function isDisputed(uint _escrowId) public view returns (bool) {
|
||||
return _isDisputed(_escrowId);
|
||||
}
|
||||
|
||||
function _isDisputed(uint _escrowId) internal view returns (bool) {
|
||||
return arbitrationCases[_escrowId].open || arbitrationCases[_escrowId].result != ArbitrationResult.UNSOLVED;
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Determine if a dispute existed for an escrow
|
||||
* @param _escrowId Escrow to verify
|
||||
* @return bool result
|
||||
*/
|
||||
function hadDispute(uint _escrowId) public view returns (bool) {
|
||||
return arbitrationCases[_escrowId].result != ArbitrationResult.UNSOLVED;
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Cancel arbitration
|
||||
* @param _escrowId Escrow to cancel
|
||||
*/
|
||||
function cancelArbitration(uint _escrowId) external {
|
||||
require(arbitrationCases[_escrowId].openBy == msg.sender, "Arbitration can only be canceled by the opener");
|
||||
require(arbitrationCases[_escrowId].result == ArbitrationResult.UNSOLVED && arbitrationCases[_escrowId].open,
|
||||
"Arbitration already solved or not open");
|
||||
|
||||
delete arbitrationCases[_escrowId];
|
||||
|
||||
emit ArbitrationCanceled(_escrowId);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Opens a dispute between a seller and a buyer
|
||||
* @param _escrowId Id of the Escrow that is being disputed
|
||||
* @param _openBy Address of the person opening the dispute (buyer or seller)
|
||||
* @param motive Description of the problem
|
||||
*/
|
||||
function _openDispute(uint _escrowId, address _openBy, string memory motive) internal {
|
||||
require(arbitrationCases[_escrowId].result == ArbitrationResult.UNSOLVED && !arbitrationCases[_escrowId].open,
|
||||
"Arbitration already solved or has been opened before");
|
||||
|
||||
address arbitratorAddress = _getArbitrator(_escrowId);
|
||||
|
||||
require(arbitratorAddress != address(0), "Arbitrator is required");
|
||||
|
||||
arbitrationCases[_escrowId] = ArbitrationCase({
|
||||
open: true,
|
||||
openBy: _openBy,
|
||||
arbitrator: arbitratorAddress,
|
||||
result: ArbitrationResult.UNSOLVED,
|
||||
motive: motive
|
||||
});
|
||||
|
||||
emit ArbitrationRequired(_escrowId);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Set arbitration result in favour of the buyer or seller and transfer funds accordingly
|
||||
* @param _escrowId Id of the escrow
|
||||
* @param _result Result of the arbitration
|
||||
*/
|
||||
function setArbitrationResult(uint _escrowId, ArbitrationResult _result) external {
|
||||
require(arbitrationCases[_escrowId].open && arbitrationCases[_escrowId].result == ArbitrationResult.UNSOLVED,
|
||||
"Case must be open and unsolved");
|
||||
require(_result != ArbitrationResult.UNSOLVED, "Arbitration does not have result");
|
||||
require(arbitratorLicenses.isLicenseOwner(msg.sender), "Only arbitrators can invoke this function");
|
||||
require(arbitrationCases[_escrowId].arbitrator == msg.sender, "Invalid escrow arbitrator");
|
||||
|
||||
arbitrationCases[_escrowId].open = false;
|
||||
arbitrationCases[_escrowId].result = _result;
|
||||
|
||||
emit ArbitrationResolved(_escrowId, _result, msg.sender);
|
||||
|
||||
if(_result == ArbitrationResult.BUYER){
|
||||
_solveDispute(_escrowId, true, msg.sender);
|
||||
} else {
|
||||
_solveDispute(_escrowId, false, msg.sender);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,230 @@
|
|||
/* solium-disable security/no-block-members */
|
||||
pragma solidity ^0.5.8;
|
||||
|
||||
import "./License.sol";
|
||||
|
||||
/**
|
||||
* @title ArbitratorLicense
|
||||
* @dev Contract for management of an arbitrator license
|
||||
*/
|
||||
contract ArbitrationLicense is License {
|
||||
|
||||
enum RequestStatus {NONE,AWAIT,ACCEPTED,REJECTED,CLOSED}
|
||||
|
||||
struct Request{
|
||||
address seller;
|
||||
address arbitrator;
|
||||
RequestStatus status;
|
||||
uint date;
|
||||
}
|
||||
|
||||
struct ArbitratorLicenseDetails {
|
||||
uint id;
|
||||
bool acceptAny;// accept any seller
|
||||
}
|
||||
|
||||
mapping(address => ArbitratorLicenseDetails) public arbitratorlicenseDetails;
|
||||
mapping(address => mapping(address => bool)) public permissions;
|
||||
mapping(address => mapping(address => bool)) public blacklist;
|
||||
mapping(bytes32 => Request) public requests;
|
||||
|
||||
event ArbitratorRequested(bytes32 id, address indexed seller, address indexed arbitrator);
|
||||
|
||||
event RequestAccepted(bytes32 id, address indexed arbitrator, address indexed seller);
|
||||
event RequestRejected(bytes32 id, address indexed arbitrator, address indexed seller);
|
||||
event RequestCanceled(bytes32 id, address indexed arbitrator, address indexed seller);
|
||||
event BlacklistSeller(address indexed arbitrator, address indexed seller);
|
||||
event UnBlacklistSeller(address indexed arbitrator, address indexed seller);
|
||||
|
||||
/**
|
||||
* @param _tokenAddress Address of token used to pay for licenses (SNT)
|
||||
* @param _price Amount of token needed to buy a license
|
||||
* @param _burnAddress Burn address where the price of the license is sent
|
||||
*/
|
||||
constructor(address _tokenAddress, uint256 _price, address _burnAddress)
|
||||
License(_tokenAddress, _price, _burnAddress)
|
||||
public {}
|
||||
|
||||
/**
|
||||
* @notice Buy an arbitrator license
|
||||
*/
|
||||
function buy() external returns(uint) {
|
||||
return _buy(msg.sender, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Buy an arbitrator license and set if the arbitrator accepts any seller
|
||||
* @param _acceptAny When set to true, all sellers are accepted by the arbitrator
|
||||
*/
|
||||
function buy(bool _acceptAny) external returns(uint) {
|
||||
return _buy(msg.sender, _acceptAny);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Buy an arbitrator license and set if the arbitrator accepts any seller. Sets the arbitrator as the address in params instead of the sender
|
||||
* @param _sender Address of the arbitrator
|
||||
* @param _acceptAny When set to true, all sellers are accepted by the arbitrator
|
||||
*/
|
||||
function _buy(address _sender, bool _acceptAny) internal returns (uint id) {
|
||||
id = _buyFrom(_sender);
|
||||
arbitratorlicenseDetails[_sender].id = id;
|
||||
arbitratorlicenseDetails[_sender].acceptAny = _acceptAny;
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Change acceptAny parameter for arbitrator
|
||||
* @param _acceptAny indicates does arbitrator allow to accept any seller/choose sellers
|
||||
*/
|
||||
function changeAcceptAny(bool _acceptAny) public {
|
||||
require(isLicenseOwner(msg.sender), "Message sender should have a valid arbitrator license");
|
||||
require(arbitratorlicenseDetails[msg.sender].acceptAny != _acceptAny,
|
||||
"Message sender should pass parameter different from the current one");
|
||||
|
||||
arbitratorlicenseDetails[msg.sender].acceptAny = _acceptAny;
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Allows arbitrator to accept a seller
|
||||
* @param _arbitrator address of a licensed arbitrator
|
||||
*/
|
||||
function requestArbitrator(address _arbitrator) public {
|
||||
require(isLicenseOwner(_arbitrator), "Arbitrator should have a valid license");
|
||||
require(!arbitratorlicenseDetails[_arbitrator].acceptAny, "Arbitrator already accepts all cases");
|
||||
|
||||
bytes32 _id = keccak256(abi.encodePacked(_arbitrator, msg.sender));
|
||||
RequestStatus _status = requests[_id].status;
|
||||
require(_status != RequestStatus.AWAIT && _status != RequestStatus.ACCEPTED, "Invalid request status");
|
||||
|
||||
if(_status == RequestStatus.REJECTED || _status == RequestStatus.CLOSED){
|
||||
require(requests[_id].date + 3 days < block.timestamp,
|
||||
"Must wait 3 days before requesting the arbitrator again");
|
||||
}
|
||||
|
||||
requests[_id] = Request({
|
||||
seller: msg.sender,
|
||||
arbitrator: _arbitrator,
|
||||
status: RequestStatus.AWAIT,
|
||||
date: block.timestamp
|
||||
});
|
||||
|
||||
emit ArbitratorRequested(_id, msg.sender, _arbitrator);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Get Request Id
|
||||
* @param _arbitrator Arbitrator address
|
||||
* @param _account Seller account
|
||||
* @return Request Id
|
||||
*/
|
||||
function getId(address _arbitrator, address _account) external pure returns(bytes32){
|
||||
return keccak256(abi.encodePacked(_arbitrator,_account));
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Allows arbitrator to accept a seller's request
|
||||
* @param _id request id
|
||||
*/
|
||||
function acceptRequest(bytes32 _id) public {
|
||||
require(isLicenseOwner(msg.sender), "Arbitrator should have a valid license");
|
||||
require(requests[_id].status == RequestStatus.AWAIT, "This request is not pending");
|
||||
require(!arbitratorlicenseDetails[msg.sender].acceptAny, "Arbitrator already accepts all cases");
|
||||
require(requests[_id].arbitrator == msg.sender, "Invalid arbitrator");
|
||||
|
||||
requests[_id].status = RequestStatus.ACCEPTED;
|
||||
|
||||
address _seller = requests[_id].seller;
|
||||
permissions[msg.sender][_seller] = true;
|
||||
|
||||
emit RequestAccepted(_id, msg.sender, requests[_id].seller);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Allows arbitrator to reject a request
|
||||
* @param _id request id
|
||||
*/
|
||||
function rejectRequest(bytes32 _id) public {
|
||||
require(isLicenseOwner(msg.sender), "Arbitrator should have a valid license");
|
||||
require(requests[_id].status == RequestStatus.AWAIT || requests[_id].status == RequestStatus.ACCEPTED,
|
||||
"Invalid request status");
|
||||
require(!arbitratorlicenseDetails[msg.sender].acceptAny, "Arbitrator accepts all cases");
|
||||
require(requests[_id].arbitrator == msg.sender, "Invalid arbitrator");
|
||||
|
||||
requests[_id].status = RequestStatus.REJECTED;
|
||||
requests[_id].date = block.timestamp;
|
||||
|
||||
address _seller = requests[_id].seller;
|
||||
permissions[msg.sender][_seller] = false;
|
||||
|
||||
emit RequestRejected(_id, msg.sender, requests[_id].seller);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Allows seller to cancel a request
|
||||
* @param _id request id
|
||||
*/
|
||||
function cancelRequest(bytes32 _id) public {
|
||||
require(requests[_id].seller == msg.sender, "This request id does not belong to the message sender");
|
||||
require(requests[_id].status == RequestStatus.AWAIT || requests[_id].status == RequestStatus.ACCEPTED, "Invalid request status");
|
||||
|
||||
address arbitrator = requests[_id].arbitrator;
|
||||
|
||||
requests[_id].status = RequestStatus.CLOSED;
|
||||
requests[_id].date = block.timestamp;
|
||||
|
||||
address _arbitrator = requests[_id].arbitrator;
|
||||
permissions[_arbitrator][msg.sender] = false;
|
||||
|
||||
emit RequestCanceled(_id, arbitrator, requests[_id].seller);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Allows arbitrator to blacklist a seller
|
||||
* @param _seller Seller address
|
||||
*/
|
||||
function blacklistSeller(address _seller) public {
|
||||
require(isLicenseOwner(msg.sender), "Arbitrator should have a valid license");
|
||||
|
||||
blacklist[msg.sender][_seller] = true;
|
||||
|
||||
emit BlacklistSeller(msg.sender, _seller);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Allows arbitrator to remove a seller from the blacklist
|
||||
* @param _seller Seller address
|
||||
*/
|
||||
function unBlacklistSeller(address _seller) public {
|
||||
require(isLicenseOwner(msg.sender), "Arbitrator should have a valid license");
|
||||
|
||||
blacklist[msg.sender][_seller] = false;
|
||||
|
||||
emit UnBlacklistSeller(msg.sender, _seller);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Checks if Arbitrator permits to use his/her services
|
||||
* @param _seller sellers's address
|
||||
* @param _arbitrator arbitrator's address
|
||||
*/
|
||||
function isAllowed(address _seller, address _arbitrator) public view returns(bool) {
|
||||
return (arbitratorlicenseDetails[_arbitrator].acceptAny && !blacklist[_arbitrator][_seller]) || permissions[_arbitrator][_seller];
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Support for "approveAndCall". Callable only by `token()`.
|
||||
* @param _from Who approved.
|
||||
* @param _amount Amount being approved, need to be equal `price()`.
|
||||
* @param _token Token being approved, need to be equal `token()`.
|
||||
* @param _data Abi encoded data with selector of `buy(and)`.
|
||||
*/
|
||||
function receiveApproval(address _from, uint256 _amount, address _token, bytes memory _data) public {
|
||||
require(_amount == price, "Wrong value");
|
||||
require(_token == address(token), "Wrong token");
|
||||
require(_token == address(msg.sender), "Wrong call");
|
||||
require(_data.length == 4, "Wrong data length");
|
||||
|
||||
require(_abiDecodeBuy(_data) == bytes4(0xa6f2ae3a), "Wrong method selector"); //bytes4(keccak256("buy()"))
|
||||
|
||||
_buy(_from, false);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,616 @@
|
|||
/* solium-disable security/no-block-members */
|
||||
/* solium-disable security/no-inline-assembly */
|
||||
|
||||
pragma solidity >=0.5.0 <0.6.0;
|
||||
|
||||
import "../common/Pausable.sol";
|
||||
import "../common/MessageSigned.sol";
|
||||
import "../token/ERC20Token.sol";
|
||||
import "./ArbitrationLicense.sol";
|
||||
import "./License.sol";
|
||||
import "./MetadataStore.sol";
|
||||
import "./Fees.sol";
|
||||
import "./Arbitrable.sol";
|
||||
import "./IEscrow.sol";
|
||||
|
||||
|
||||
/**
|
||||
* @title Escrow
|
||||
* @dev Escrow contract for selling ETH and ERC20 tokens
|
||||
*/
|
||||
contract Escrow is IEscrow, Pausable, MessageSigned, Fees, Arbitrable {
|
||||
|
||||
EscrowTransaction[] public transactions;
|
||||
|
||||
address public relayer;
|
||||
License public sellerLicenses;
|
||||
MetadataStore public metadataStore;
|
||||
|
||||
event Created(uint indexed offerId, address indexed seller, address indexed buyer, uint escrowId);
|
||||
event Funded(uint indexed escrowId, uint expirationTime, uint amount);
|
||||
event Paid(uint indexed escrowId);
|
||||
event Released(uint indexed escrowId);
|
||||
event Canceled(uint indexed escrowId);
|
||||
event Rating(uint indexed offerId, address indexed buyer, uint indexed escrowId, uint rating);
|
||||
|
||||
bool internal _initialized;
|
||||
|
||||
/**
|
||||
* @param _relayer EscrowRelay contract address
|
||||
* @param _sellerLicenses License contract instance address for sellers
|
||||
* @param _arbitratorLicenses License contract instance address for arbitrators
|
||||
* @param _metadataStore MetadataStore contract address
|
||||
* @param _feeDestination Address where the fees are going to be sent
|
||||
* @param _feeMilliPercent Percentage applied as a fee to each escrow. 1000 == 1%
|
||||
*/
|
||||
constructor(
|
||||
address _relayer,
|
||||
address _sellerLicenses,
|
||||
address _arbitratorLicenses,
|
||||
address _metadataStore,
|
||||
address payable _feeDestination,
|
||||
uint _feeMilliPercent)
|
||||
Fees(_feeDestination, _feeMilliPercent)
|
||||
Arbitrable(_arbitratorLicenses)
|
||||
public {
|
||||
_initialized = true;
|
||||
relayer = _relayer;
|
||||
sellerLicenses = License(_sellerLicenses);
|
||||
metadataStore = MetadataStore(_metadataStore);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Initialize contract (used with proxy). Can only be called once
|
||||
* @param _relayer EscrowRelay contract address
|
||||
* @param _sellerLicenses License contract instance address for sellers
|
||||
* @param _arbitratorLicenses License contract instance address for arbitrators
|
||||
* @param _metadataStore MetadataStore contract address
|
||||
* @param _feeDestination Address where the fees are going to be sent
|
||||
* @param _feeMilliPercent Percentage applied as a fee to each escrow. 1000 == 1%
|
||||
*/
|
||||
function init(
|
||||
address _relayer,
|
||||
address _sellerLicenses,
|
||||
address _arbitratorLicenses,
|
||||
address _metadataStore,
|
||||
address payable _feeDestination,
|
||||
uint _feeMilliPercent
|
||||
) external {
|
||||
assert(_initialized == false);
|
||||
|
||||
_initialized = true;
|
||||
|
||||
sellerLicenses = License(_sellerLicenses);
|
||||
arbitratorLicenses = ArbitrationLicense(_arbitratorLicenses);
|
||||
metadataStore = MetadataStore(_metadataStore);
|
||||
relayer = _relayer;
|
||||
feeDestination = _feeDestination;
|
||||
feeMilliPercent = _feeMilliPercent;
|
||||
paused = false;
|
||||
_setOwner(msg.sender);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Update relayer contract address. Can only be called by the contract owner
|
||||
* @param _relayer EscrowRelay contract address
|
||||
*/
|
||||
function setRelayer(address _relayer) external onlyOwner {
|
||||
relayer = _relayer;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Update license contract addresses
|
||||
* @param _sellerLicenses License contract instance address for sellers
|
||||
* @param _arbitratorLicenses License contract instance address for arbitrators
|
||||
*/
|
||||
function setLicenses(address _sellerLicenses, address _arbitratorLicenses) external onlyOwner {
|
||||
sellerLicenses = License(_sellerLicenses);
|
||||
arbitratorLicenses = ArbitrationLicense(_arbitratorLicenses);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Update MetadataStore contract address
|
||||
* @param _metadataStore MetadataStore contract address
|
||||
*/
|
||||
function setMetadataStore(address _metadataStore) external onlyOwner {
|
||||
metadataStore = MetadataStore(_metadataStore);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Escrow creation logic. Requires contract to be unpaused
|
||||
* @param _buyer Buyer Address
|
||||
* @param _offerId Offer
|
||||
* @param _tokenAmount Amount buyer is willing to trade
|
||||
* @param _assetPrice Indicates the price of the asset in the FIAT of choice
|
||||
* @return Id of the Escrow
|
||||
*/
|
||||
function _createTransaction(
|
||||
address payable _buyer,
|
||||
uint _offerId,
|
||||
uint _tokenAmount,
|
||||
uint _assetPrice
|
||||
) internal whenNotPaused returns(uint escrowId)
|
||||
{
|
||||
address payable seller;
|
||||
address payable arbitrator;
|
||||
bool deleted;
|
||||
address token;
|
||||
|
||||
(token, , , , , , seller, arbitrator, deleted) = metadataStore.offer(_offerId);
|
||||
|
||||
require(!deleted, "Offer is not valid");
|
||||
require(sellerLicenses.isLicenseOwner(seller), "Must be a valid seller to create escrow transactions");
|
||||
require(seller != _buyer, "Seller and Buyer must be different");
|
||||
require(arbitrator != _buyer && arbitrator != address(0), "Cannot buy offers where buyer is arbitrator");
|
||||
require(_tokenAmount != 0 && _assetPrice != 0, "Trade amounts cannot be 0");
|
||||
|
||||
escrowId = transactions.length++;
|
||||
|
||||
EscrowTransaction storage trx = transactions[escrowId];
|
||||
|
||||
trx.offerId = _offerId;
|
||||
trx.token = token;
|
||||
trx.buyer = _buyer;
|
||||
trx.seller = seller;
|
||||
trx.arbitrator = arbitrator;
|
||||
trx.tokenAmount = _tokenAmount;
|
||||
trx.assetPrice = _assetPrice;
|
||||
|
||||
emit Created(
|
||||
_offerId,
|
||||
seller,
|
||||
_buyer,
|
||||
escrowId
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Create a new escrow
|
||||
* @param _offerId Offer
|
||||
* @param _tokenAmount Amount buyer is willing to trade
|
||||
* @param _assetPrice Indicates the price of the asset in the FIAT of choice
|
||||
* @param _pubkeyA First coordinate of Status Whisper Public Key
|
||||
* @param _pubkeyB Second coordinate of Status Whisper Public Key
|
||||
* @param _location The location on earth
|
||||
* @param _username The username of the user
|
||||
* @param _nonce The nonce for the user (from MetadataStore.user_nonce(address))
|
||||
* @param _signature buyer's signature
|
||||
* @return Id of the new escrow
|
||||
* @dev Requires contract to be unpaused.
|
||||
* The seller needs to be licensed.
|
||||
*/
|
||||
function createEscrow(
|
||||
uint _offerId,
|
||||
uint _tokenAmount,
|
||||
uint _assetPrice,
|
||||
bytes32 _pubkeyA,
|
||||
bytes32 _pubkeyB,
|
||||
string memory _location,
|
||||
string memory _username,
|
||||
uint _nonce,
|
||||
bytes memory _signature
|
||||
) public returns(uint escrowId) {
|
||||
address payable _buyer = metadataStore.addOrUpdateUser(_signature, _pubkeyA, _pubkeyB, _location, _username, _nonce);
|
||||
escrowId = _createTransaction(_buyer, _offerId, _tokenAmount, _assetPrice);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Fund a new escrow
|
||||
* @param _escrowId Id of the escrow
|
||||
* @dev Requires contract to be unpaused.
|
||||
* The seller needs to be licensed.
|
||||
* The expiration time must be at least 10min in the future
|
||||
* For eth transfer, _amount must be equals to msg.value, for token transfer, requires an allowance and transfer valid for _amount
|
||||
*/
|
||||
function fund(uint _escrowId) external payable whenNotPaused {
|
||||
_fund(msg.sender, _escrowId);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Escrow funding logic
|
||||
* @param _from Seller address
|
||||
* @param _escrowId Id of the escrow
|
||||
* @dev Requires contract to be unpaused.
|
||||
* The seller needs to be licensed.
|
||||
* The expiration time must be at least 10min in the future
|
||||
* For eth transfer, _amount must be equals to msg.value, for token transfer, requires an allowance and transfer valid for _amount
|
||||
*/
|
||||
function _fund(address _from, uint _escrowId) internal whenNotPaused {
|
||||
require(transactions[_escrowId].seller == _from, "Only the seller can invoke this function");
|
||||
require(transactions[_escrowId].status == EscrowStatus.CREATED, "Invalid escrow status");
|
||||
|
||||
require(sellerLicenses.isLicenseOwner(_from), "Must be a valid seller to fund escrow transactions");
|
||||
|
||||
transactions[_escrowId].expirationTime = block.timestamp + 5 days;
|
||||
transactions[_escrowId].status = EscrowStatus.FUNDED;
|
||||
|
||||
uint tokenAmount = transactions[_escrowId].tokenAmount;
|
||||
|
||||
address token = transactions[_escrowId].token;
|
||||
|
||||
_payFee(_from, _escrowId, tokenAmount, token);
|
||||
|
||||
emit Funded(_escrowId, block.timestamp + 5 days, tokenAmount);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Create and fund a new escrow, as a seller, once you get a buyer signature
|
||||
* @param _offerId Offer
|
||||
* @param _tokenAmount Amount buyer is willing to trade
|
||||
* @param _assetPrice Indicates the price of the asset in the FIAT of choice
|
||||
* @param _bPubkeyA First coordinate of Status Whisper Public Key
|
||||
* @param _bPubkeyB Second coordinate of Status Whisper Public Key
|
||||
* @param _bLocation The location on earth
|
||||
* @param _bUsername The username of the user
|
||||
* @param _bNonce The nonce for the user (from MetadataStore.user_nonce(address))
|
||||
* @param _bSignature buyer's signature
|
||||
* @return Id of the new escrow
|
||||
* @dev Requires contract to be unpaused.
|
||||
* Restrictions from escrow creation and funding applies
|
||||
*/
|
||||
function createAndFund (
|
||||
uint _offerId,
|
||||
uint _tokenAmount,
|
||||
uint _assetPrice,
|
||||
bytes32 _bPubkeyA,
|
||||
bytes32 _bPubkeyB,
|
||||
string memory _bLocation,
|
||||
string memory _bUsername,
|
||||
uint _bNonce,
|
||||
bytes memory _bSignature
|
||||
) public payable returns(uint escrowId) {
|
||||
address payable _buyer = metadataStore.addOrUpdateUser(_bSignature, _bPubkeyA, _bPubkeyB, _bLocation, _bUsername, _bNonce);
|
||||
escrowId = _createTransaction(_buyer, _offerId, _tokenAmount, _assetPrice);
|
||||
_fund(msg.sender, escrowId);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Buyer marks transaction as paid
|
||||
* @param _sender Address marking the transaction as paid
|
||||
* @param _escrowId Id of the escrow
|
||||
*/
|
||||
function _pay(address _sender, uint _escrowId) internal {
|
||||
EscrowTransaction storage trx = transactions[_escrowId];
|
||||
|
||||
require(trx.status == EscrowStatus.FUNDED, "Transaction is not funded");
|
||||
require(trx.expirationTime > block.timestamp, "Transaction already expired");
|
||||
require(trx.buyer == _sender, "Only the buyer can invoke this function");
|
||||
|
||||
trx.status = EscrowStatus.PAID;
|
||||
|
||||
emit Paid(_escrowId);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Mark transaction as paid
|
||||
* @param _escrowId Id of the escrow
|
||||
* @dev Can only be executed by the buyer
|
||||
*/
|
||||
function pay(uint _escrowId) external {
|
||||
_pay(msg.sender, _escrowId);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Relay function for marking a transaction as paid
|
||||
* Can only be called by relayer address
|
||||
* @param _sender Address marking the transaction as paid
|
||||
* @param _escrowId Id of the escrow
|
||||
*/
|
||||
function pay_relayed(address _sender, uint _escrowId) external {
|
||||
assert(msg.sender == relayer);
|
||||
_pay(_sender, _escrowId);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Obtain message hash to be signed for marking a transaction as paid
|
||||
* @param _escrowId Id of the escrow
|
||||
* @return message hash
|
||||
* @dev Once message is signed, pass it as _signature of pay(uint256,bytes)
|
||||
*/
|
||||
function paySignHash(uint _escrowId) public view returns(bytes32){
|
||||
return keccak256(
|
||||
abi.encodePacked(
|
||||
address(this),
|
||||
"pay(uint256)",
|
||||
_escrowId
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Mark transaction as paid (via signed message)
|
||||
* @param _escrowId Id of the escrow
|
||||
* @param _signature Signature of the paySignHash result.
|
||||
* @dev There's a high probability of buyers not having ether to pay for the transaction.
|
||||
* This allows anyone to relay the transaction.
|
||||
* TODO: consider deducting funds later on release to pay the relayer (?)
|
||||
*/
|
||||
function pay(uint _escrowId, bytes calldata _signature) external {
|
||||
address sender = _recoverAddress(_getSignHash(paySignHash(_escrowId)), _signature);
|
||||
_pay(sender, _escrowId);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Release funds to buyer
|
||||
* @param _escrowId Id of the escrow
|
||||
* @param _trx EscrowTransaction with data of transaction to be released
|
||||
* @param _isDispute indicates if the release happened due to a dispute
|
||||
*/
|
||||
function _release(uint _escrowId, EscrowTransaction storage _trx, bool _isDispute) internal {
|
||||
_trx.status = EscrowStatus.RELEASED;
|
||||
address token = _trx.token;
|
||||
if(token == address(0)){
|
||||
_trx.buyer.transfer(_trx.tokenAmount);
|
||||
} else {
|
||||
require(ERC20Token(token).transfer(_trx.buyer, _trx.tokenAmount), "Couldn't transfer funds");
|
||||
}
|
||||
_releaseFee(_trx.arbitrator, _trx.tokenAmount, token, _isDispute);
|
||||
|
||||
emit Released(_escrowId);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Release escrow funds to buyer
|
||||
* @param _escrowId Id of the escrow
|
||||
* @dev Requires contract to be unpaused.
|
||||
* Can only be executed by the seller
|
||||
* Transaction must not be expired, or previously canceled or released
|
||||
*/
|
||||
function release(uint _escrowId) external {
|
||||
EscrowStatus mStatus = transactions[_escrowId].status;
|
||||
require(transactions[_escrowId].seller == msg.sender, "Only the seller can invoke this function");
|
||||
require(mStatus == EscrowStatus.PAID || mStatus == EscrowStatus.FUNDED, "Invalid transaction status");
|
||||
require(!_isDisputed(_escrowId), "Can't release a transaction that has an arbitration process");
|
||||
_release(_escrowId, transactions[_escrowId], false);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Cancel an escrow operation
|
||||
* @param _escrowId Id of the escrow
|
||||
* @notice Requires contract to be unpaused.
|
||||
* Can only be executed by the seller
|
||||
* Transaction must be expired, or previously canceled or released
|
||||
*/
|
||||
function cancel(uint _escrowId) external whenNotPaused {
|
||||
EscrowTransaction storage trx = transactions[_escrowId];
|
||||
|
||||
EscrowStatus mStatus = trx.status;
|
||||
require(mStatus == EscrowStatus.FUNDED || mStatus == EscrowStatus.CREATED,
|
||||
"Only transactions in created or funded state can be canceled");
|
||||
|
||||
require(trx.buyer == msg.sender || trx.seller == msg.sender, "Only participants can invoke this function");
|
||||
|
||||
if(mStatus == EscrowStatus.FUNDED){
|
||||
if(msg.sender == trx.seller){
|
||||
require(trx.expirationTime < block.timestamp, "Can only be canceled after expiration");
|
||||
}
|
||||
}
|
||||
|
||||
_cancel(_escrowId, trx, false);
|
||||
}
|
||||
|
||||
// Same as cancel, but relayed by a contract so we get the sender as param
|
||||
function cancel_relayed(address _sender, uint _escrowId) external {
|
||||
assert(msg.sender == relayer);
|
||||
|
||||
EscrowTransaction storage trx = transactions[_escrowId];
|
||||
EscrowStatus mStatus = trx.status;
|
||||
require(trx.buyer == _sender, "Only the buyer can invoke this function");
|
||||
require(mStatus == EscrowStatus.FUNDED || mStatus == EscrowStatus.CREATED,
|
||||
"Only transactions in created or funded state can be canceled");
|
||||
|
||||
_cancel(_escrowId, trx, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Cancel transaction and send funds back to seller
|
||||
* @param _escrowId Id of the escrow
|
||||
* @param trx EscrowTransaction with details of transaction to be marked as canceled
|
||||
*/
|
||||
function _cancel(uint _escrowId, EscrowTransaction storage trx, bool isDispute) internal {
|
||||
if(trx.status == EscrowStatus.FUNDED){
|
||||
address token = trx.token;
|
||||
uint amount = trx.tokenAmount;
|
||||
if (!isDispute) {
|
||||
amount += _getValueOffMillipercent(trx.tokenAmount, feeMilliPercent);
|
||||
}
|
||||
|
||||
if(token == address(0)){
|
||||
trx.seller.transfer(amount);
|
||||
} else {
|
||||
ERC20Token erc20token = ERC20Token(token);
|
||||
require(erc20token.transfer(trx.seller, amount), "Transfer failed");
|
||||
}
|
||||
}
|
||||
|
||||
trx.status = EscrowStatus.CANCELED;
|
||||
emit Canceled(_escrowId);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @notice Rates a transaction
|
||||
* @param _escrowId Id of the escrow
|
||||
* @param _rate rating of the transaction from 1 to 5
|
||||
* @dev Can only be executed by the buyer
|
||||
* Transaction must released
|
||||
*/
|
||||
function rateTransaction(uint _escrowId, uint _rate) external {
|
||||
require(_rate >= 1, "Rating needs to be at least 1");
|
||||
require(_rate <= 5, "Rating needs to be at less than or equal to 5");
|
||||
EscrowTransaction storage trx = transactions[_escrowId];
|
||||
|
||||
require(trx.rating == 0, "Transaction already rated");
|
||||
require(trx.status == EscrowStatus.RELEASED || hadDispute(_escrowId), "Transaction not completed yet");
|
||||
require(trx.buyer == msg.sender, "Only the buyer can invoke this function");
|
||||
|
||||
trx.rating = _rate;
|
||||
|
||||
emit Rating(trx.offerId, trx.buyer, _escrowId, _rate);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Returns basic trade informations (buyer address, seller address, token address and token amount)
|
||||
* @param _escrowId Id of the escrow
|
||||
*/
|
||||
function getBasicTradeData(uint _escrowId)
|
||||
external
|
||||
view
|
||||
returns(address payable buyer, address payable seller, address token, uint tokenAmount) {
|
||||
buyer = transactions[_escrowId].buyer;
|
||||
seller = transactions[_escrowId].seller;
|
||||
tokenAmount = transactions[_escrowId].tokenAmount;
|
||||
token = transactions[_escrowId].token;
|
||||
|
||||
return (buyer, seller, token, tokenAmount);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Open case as a buyer or seller for arbitration
|
||||
* @param _escrowId Id of the escrow
|
||||
* @param _motive Motive for opening the dispute
|
||||
*/
|
||||
function openCase(uint _escrowId, string calldata _motive) external {
|
||||
EscrowTransaction storage trx = transactions[_escrowId];
|
||||
|
||||
require(!isDisputed(_escrowId), "Case already exist");
|
||||
require(trx.buyer == msg.sender || trx.seller == msg.sender, "Only participants can invoke this function");
|
||||
require(trx.status == EscrowStatus.PAID, "Cases can only be open for paid transactions");
|
||||
|
||||
_openDispute(_escrowId, msg.sender, _motive);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Open case via relayer
|
||||
* @param _sender Address initiating the relayed transaction
|
||||
* @param _escrowId Id of the escrow
|
||||
* @param _motive Motive for opening the dispute
|
||||
*/
|
||||
function openCase_relayed(address _sender, uint256 _escrowId, string calldata _motive) external {
|
||||
assert(msg.sender == relayer);
|
||||
|
||||
EscrowTransaction storage trx = transactions[_escrowId];
|
||||
|
||||
require(!isDisputed(_escrowId), "Case already exist");
|
||||
require(trx.buyer == _sender, "Only the buyer can invoke this function");
|
||||
require(trx.status == EscrowStatus.PAID, "Cases can only be open for paid transactions");
|
||||
|
||||
_openDispute(_escrowId, _sender, _motive);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Open case as a buyer or seller for arbitration via a relay account
|
||||
* @param _escrowId Id of the escrow
|
||||
* @param _motive Motive for opening the dispute
|
||||
* @param _signature Signed message result of openCaseSignHash(uint256)
|
||||
* @dev Consider opening a dispute in aragon court.
|
||||
*/
|
||||
function openCase(uint _escrowId, string calldata _motive, bytes calldata _signature) external {
|
||||
EscrowTransaction storage trx = transactions[_escrowId];
|
||||
|
||||
require(!isDisputed(_escrowId), "Case already exist");
|
||||
require(trx.status == EscrowStatus.PAID, "Cases can only be open for paid transactions");
|
||||
|
||||
address senderAddress = _recoverAddress(_getSignHash(openCaseSignHash(_escrowId, _motive)), _signature);
|
||||
|
||||
require(trx.buyer == senderAddress || trx.seller == senderAddress, "Only participants can invoke this function");
|
||||
|
||||
_openDispute(_escrowId, msg.sender, _motive);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Set arbitration result in favour of the buyer or seller and transfer funds accordingly
|
||||
* @param _escrowId Id of the escrow
|
||||
* @param _releaseFunds Release funds to buyer or cancel escrow
|
||||
* @param _arbitrator Arbitrator address
|
||||
*/
|
||||
function _solveDispute(uint _escrowId, bool _releaseFunds, address _arbitrator) internal {
|
||||
EscrowTransaction storage trx = transactions[_escrowId];
|
||||
|
||||
require(trx.buyer != _arbitrator && trx.seller != _arbitrator, "Arbitrator cannot be part of transaction");
|
||||
|
||||
if(_releaseFunds){
|
||||
_release(_escrowId, trx, true);
|
||||
} else {
|
||||
_cancel(_escrowId, trx, true);
|
||||
_releaseFee(trx.arbitrator, trx.tokenAmount, trx.token, true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Get arbitrator
|
||||
* @param _escrowId Id of the escrow
|
||||
* @return Arbitrator address
|
||||
*/
|
||||
function _getArbitrator(uint _escrowId) internal view returns(address) {
|
||||
return transactions[_escrowId].arbitrator;
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Obtain message hash to be signed for opening a case
|
||||
* @param _escrowId Id of the escrow
|
||||
* @return message hash
|
||||
* @dev Once message is signed, pass it as _signature of openCase(uint256,bytes)
|
||||
*/
|
||||
function openCaseSignHash(uint _escrowId, string memory motive) public view returns(bytes32){
|
||||
return keccak256(
|
||||
abi.encodePacked(
|
||||
address(this),
|
||||
"openCase(uint256)",
|
||||
_escrowId,
|
||||
motive
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Support for "approveAndCall". Callable only by the fee token.
|
||||
* @param _from Who approved.
|
||||
* @param _amount Amount being approved, need to be equal `getPrice()`.
|
||||
* @param _token Token being approved, need to be equal `token()`.
|
||||
* @param _data Abi encoded data with selector of `register(bytes32,address,bytes32,bytes32)`.
|
||||
*/
|
||||
function receiveApproval(address _from, uint256 _amount, address _token, bytes memory _data) public {
|
||||
require(_token == address(msg.sender), "Wrong call");
|
||||
require(_data.length == 36, "Wrong data length");
|
||||
|
||||
bytes4 sig;
|
||||
uint256 escrowId;
|
||||
|
||||
(sig, escrowId) = _abiDecodeFundCall(_data);
|
||||
|
||||
if (sig == bytes4(0xca1d209d)){ // fund(uint256)
|
||||
uint tokenAmount = transactions[escrowId].tokenAmount;
|
||||
require(_amount == tokenAmount + _getValueOffMillipercent(tokenAmount, feeMilliPercent), "Invalid amount");
|
||||
_fund(_from, escrowId);
|
||||
} else {
|
||||
revert("Wrong method selector");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Decodes abi encoded data with selector for "fund".
|
||||
* @param _data Abi encoded data.
|
||||
* @return Decoded registry call.
|
||||
*/
|
||||
function _abiDecodeFundCall(bytes memory _data) internal pure returns (bytes4 sig, uint256 escrowId) {
|
||||
assembly {
|
||||
sig := mload(add(_data, add(0x20, 0)))
|
||||
escrowId := mload(add(_data, 36))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Withdraws funds to the sellers in case of emergency
|
||||
* Requires contract to be paused.
|
||||
* Can be executed by anyone
|
||||
* Transaction must not be canceled or released
|
||||
* @param _escrowId Id of the Escrow
|
||||
*/
|
||||
function withdraw_emergency(uint _escrowId) external whenPaused {
|
||||
EscrowTransaction storage trx = transactions[_escrowId];
|
||||
require(trx.status == EscrowStatus.FUNDED, "Cannot withdraw from escrow in a stage different from FUNDED. Open a case");
|
||||
|
||||
_cancel(_escrowId, trx, false);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,243 @@
|
|||
/* solium-disable security/no-block-members */
|
||||
/* solium-disable security/no-inline-assembly */
|
||||
/* solium-disable no-empty-blocks */
|
||||
|
||||
pragma solidity >=0.5.0 <0.6.0;
|
||||
|
||||
import "./IEscrow.sol";
|
||||
import "./MetadataStore.sol";
|
||||
import "../common/Ownable.sol";
|
||||
import "tabookey-gasless/contracts/RelayRecipient.sol";
|
||||
|
||||
/**
|
||||
* @title Escrow Relay (Gas Station Network)
|
||||
*/
|
||||
contract EscrowRelay is RelayRecipient, Ownable {
|
||||
|
||||
MetadataStore public metadataStore;
|
||||
IEscrow public escrow;
|
||||
address public snt;
|
||||
|
||||
mapping(address => uint) public lastActivity;
|
||||
|
||||
bytes4 constant CREATE_SIGNATURE = bytes4(keccak256("createEscrow(uint256,uint256,uint256,bytes32,bytes32,string,string,uint256,bytes)"));
|
||||
bytes4 constant PAY_SIGNATURE = bytes4(keccak256("pay(uint256)"));
|
||||
bytes4 constant CANCEL_SIGNATURE = bytes4(keccak256("cancel(uint256)"));
|
||||
bytes4 constant OPEN_CASE_SIGNATURE = bytes4(keccak256("openCase(uint256,string)"));
|
||||
|
||||
uint256 constant OK = 0;
|
||||
uint256 constant ERROR_ENOUGH_BALANCE = 11;
|
||||
uint256 constant ERROR_INVALID_ASSET = 12;
|
||||
uint256 constant ERROR_TRX_TOO_SOON = 13;
|
||||
uint256 constant ERROR_INVALID_BUYER = 14;
|
||||
uint256 constant ERROR_GAS_PRICE = 15;
|
||||
uint256 constant ERROR = 99;
|
||||
|
||||
/**
|
||||
* @param _metadataStore Metadata Store Address
|
||||
* @param _escrow IEscrow Instance Address
|
||||
* @param _snt SNT address
|
||||
*/
|
||||
constructor(address _metadataStore, address _escrow, address _snt) public {
|
||||
metadataStore = MetadataStore(_metadataStore);
|
||||
escrow = IEscrow(_escrow);
|
||||
snt = _snt;
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Set metadata store address
|
||||
* @dev Only contract owner can execute this function
|
||||
* @param _metadataStore New metadata store address
|
||||
*/
|
||||
function setMetadataStore(address _metadataStore) external onlyOwner {
|
||||
metadataStore = MetadataStore(_metadataStore);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Set escrow address
|
||||
* @dev Only contract owner can execute this function
|
||||
* @param _escrow New escrow address
|
||||
*/
|
||||
function setEscrow(address _escrow) external onlyOwner {
|
||||
escrow = IEscrow(_escrow);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Set gas station network hub address
|
||||
* @dev Only contract owner can execute this function
|
||||
* @param _relayHub New relay hub address
|
||||
*/
|
||||
function setRelayHubAddress(address _relayHub) external onlyOwner {
|
||||
setRelayHub(IRelayHub(_relayHub));
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Determine if the timeout for relaying a create/cancel transaction has passed
|
||||
* @param _account Account to verify
|
||||
* @return bool
|
||||
*/
|
||||
function canCreateOrCancel(address _account) external view returns(bool) {
|
||||
return (lastActivity[_account] + 15 minutes) < block.timestamp;
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Create a new escrow
|
||||
* @param _offerId Offer
|
||||
* @param _tokenAmount Amount buyer is willing to trade
|
||||
* @param _assetPrice Indicates the price of the asset in the FIAT of choice
|
||||
* @param _pubkeyA First coordinate of Status Whisper Public Key
|
||||
* @param _pubkeyB Second coordinate of Status Whisper Public Key
|
||||
* @param _location The location on earth
|
||||
* @param _username The username of the user
|
||||
* @param _nonce buyer's nonce
|
||||
* @param _signature buyer's signature
|
||||
*/
|
||||
function createEscrow(
|
||||
uint _offerId,
|
||||
uint _tokenAmount,
|
||||
uint _assetPrice,
|
||||
bytes32 _pubkeyA,
|
||||
bytes32 _pubkeyB,
|
||||
string memory _location,
|
||||
string memory _username,
|
||||
uint _nonce,
|
||||
bytes memory _signature
|
||||
) public returns (uint escrowId) {
|
||||
lastActivity[getSender()] = block.timestamp;
|
||||
escrowId = escrow.createEscrow(
|
||||
_offerId,
|
||||
_tokenAmount,
|
||||
_assetPrice,
|
||||
_pubkeyA,
|
||||
_pubkeyB,
|
||||
_location,
|
||||
_username,
|
||||
_nonce,
|
||||
_signature
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Mark transaction as paid
|
||||
* @param _escrowId Escrow to mark as paid
|
||||
*/
|
||||
function pay(uint _escrowId) external {
|
||||
address sender = getSender();
|
||||
escrow.pay_relayed(sender, _escrowId);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Cancel an escrow
|
||||
* @param _escrowId Escrow to cancel
|
||||
*/
|
||||
function cancel(uint _escrowId) external {
|
||||
address sender = getSender();
|
||||
lastActivity[sender] = block.timestamp;
|
||||
escrow.cancel_relayed(sender, _escrowId);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Open a dispute
|
||||
* @param _escrowId Escrow to open a dispute
|
||||
* @param _motive Motive a dispute is being opened
|
||||
*/
|
||||
function openCase(uint _escrowId, string memory _motive) public {
|
||||
address sender = getSender();
|
||||
escrow.openCase_relayed(sender, _escrowId, _motive);
|
||||
}
|
||||
|
||||
// =======================1=================================================
|
||||
// Gas station network
|
||||
|
||||
/**
|
||||
* @notice Function returning if we accept or not the relayed call (do we pay or not for the gas)
|
||||
* @param from Address of the buyer getting a free transaction
|
||||
* @param encodedFunction Function that will be called on the Escrow contract
|
||||
* @param gasPrice Gas price
|
||||
* @dev relay and transaction_fee are useless in our relay workflow
|
||||
*/
|
||||
function acceptRelayedCall(
|
||||
address /* relay */,
|
||||
address from,
|
||||
bytes calldata encodedFunction,
|
||||
uint256 /* transactionFee */,
|
||||
uint256 gasPrice,
|
||||
uint256 /* gasLimit */,
|
||||
uint256 /* nonce */,
|
||||
bytes calldata /* approvalData */,
|
||||
uint256 /* maxPossibleCharge */
|
||||
) external view returns (uint256, bytes memory)
|
||||
{
|
||||
bytes memory abiEncodedFunc = encodedFunction; // Call data elements cannot be accessed directly
|
||||
bytes4 fSign;
|
||||
uint dataValue;
|
||||
|
||||
assembly {
|
||||
fSign := mload(add(abiEncodedFunc, add(0x20, 0)))
|
||||
dataValue := mload(add(abiEncodedFunc, 36))
|
||||
}
|
||||
|
||||
return (_evaluateConditionsToRelay(from, gasPrice, fSign, dataValue), "");
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Evaluates if the sender conditions are valid for relaying a escrow transaction
|
||||
* @param from Sender
|
||||
* @param gasPrice Gas Price
|
||||
* @param functionSignature Function Signature
|
||||
* @param dataValue Represents the escrowId or offerId depending on the function being called
|
||||
*/
|
||||
function _evaluateConditionsToRelay(address from, uint gasPrice, bytes4 functionSignature, uint dataValue) internal view returns (uint256) {
|
||||
address token;
|
||||
|
||||
if(from.balance > 600000 * gasPrice) return ERROR_ENOUGH_BALANCE;
|
||||
|
||||
if(gasPrice > 20000000000) return ERROR_GAS_PRICE; // 20Gwei
|
||||
|
||||
if(functionSignature == PAY_SIGNATURE || functionSignature == CANCEL_SIGNATURE || functionSignature == OPEN_CASE_SIGNATURE){
|
||||
address payable buyer;
|
||||
|
||||
(buyer, , token, ) = escrow.getBasicTradeData(dataValue);
|
||||
|
||||
if(buyer != from) return ERROR_INVALID_BUYER;
|
||||
if(token != snt && token != address(0)) return ERROR_INVALID_ASSET;
|
||||
|
||||
if(functionSignature == CANCEL_SIGNATURE){ // Allow activity after 15min have passed
|
||||
if((lastActivity[from] + 15 minutes) > block.timestamp) return ERROR_TRX_TOO_SOON;
|
||||
}
|
||||
|
||||
return OK;
|
||||
} else if(functionSignature == CREATE_SIGNATURE) {
|
||||
token = metadataStore.getAsset(dataValue);
|
||||
|
||||
if(token != snt && token != address(0)) return ERROR_INVALID_ASSET;
|
||||
|
||||
// Allow activity after 15 min have passed
|
||||
if((lastActivity[from] + 15 minutes) > block.timestamp) return ERROR_TRX_TOO_SOON;
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
return ERROR;
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Function executed before the relay. Unused by us
|
||||
*/
|
||||
function preRelayedCall(bytes calldata) external returns (bytes32){
|
||||
// nothing to be done pre-call.
|
||||
// still, we must implement this method.
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Function executed after the relay. Unused by us
|
||||
*/
|
||||
function postRelayedCall(
|
||||
bytes calldata /*context*/,
|
||||
bool /*success*/,
|
||||
uint /*actualCharge*/,
|
||||
bytes32 /*preRetVal*/) external {
|
||||
// nothing to be done post-call.
|
||||
// still, we must implement this method.
|
||||
}
|
||||
}
|
|
@ -0,0 +1,117 @@
|
|||
pragma solidity >=0.5.0 <0.6.0;
|
||||
|
||||
import "../token/ERC20Token.sol";
|
||||
import "../common/Ownable.sol";
|
||||
|
||||
/**
|
||||
* @title Fee utilities
|
||||
* @dev Fee registry, payment and withdraw utilities.
|
||||
*/
|
||||
contract Fees is Ownable {
|
||||
address payable public feeDestination;
|
||||
uint public feeMilliPercent;
|
||||
mapping(address => uint) public feeTokenBalances;
|
||||
mapping(uint => bool) public feePaid;
|
||||
|
||||
event FeeDestinationChanged(address payable);
|
||||
event FeeMilliPercentChanged(uint amount);
|
||||
event FeesWithdrawn(uint amount, address token);
|
||||
|
||||
/**
|
||||
* @param _feeDestination Address to send the fees once withdraw is called
|
||||
* @param _feeMilliPercent Millipercent for the fee off teh amount sold
|
||||
*/
|
||||
constructor(address payable _feeDestination, uint _feeMilliPercent) public {
|
||||
feeDestination = _feeDestination;
|
||||
feeMilliPercent = _feeMilliPercent;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Set Fee Destination Address.
|
||||
* Can only be called by the owner of the contract
|
||||
* @param _addr New address
|
||||
*/
|
||||
function setFeeDestinationAddress(address payable _addr) external onlyOwner {
|
||||
feeDestination = _addr;
|
||||
emit FeeDestinationChanged(_addr);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Set Fee Amount
|
||||
* Can only be called by the owner of the contract
|
||||
* @param _feeMilliPercent New millipercent
|
||||
*/
|
||||
function setFeeAmount(uint _feeMilliPercent) external onlyOwner {
|
||||
feeMilliPercent = _feeMilliPercent;
|
||||
emit FeeMilliPercentChanged(_feeMilliPercent);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Release fee to fee destination and arbitrator
|
||||
* @param _arbitrator Arbitrator address to transfer fee to
|
||||
* @param _value Value sold in the escrow
|
||||
* @param _isDispute Boolean telling if it was from a dispute. With a dispute, the arbitrator gets more
|
||||
*/
|
||||
function _releaseFee(address payable _arbitrator, uint _value, address _tokenAddress, bool _isDispute) internal {
|
||||
uint _milliPercentToArbitrator;
|
||||
if (_isDispute) {
|
||||
_milliPercentToArbitrator = 100000; // 100%
|
||||
} else {
|
||||
_milliPercentToArbitrator = 10000; // 10%
|
||||
}
|
||||
|
||||
uint feeAmount = _getValueOffMillipercent(_value, feeMilliPercent);
|
||||
uint arbitratorValue = _getValueOffMillipercent(feeAmount, _milliPercentToArbitrator);
|
||||
uint destinationValue = feeAmount - arbitratorValue;
|
||||
|
||||
if (_tokenAddress != address(0)) {
|
||||
ERC20Token tokenToPay = ERC20Token(_tokenAddress);
|
||||
require(tokenToPay.transfer(_arbitrator, arbitratorValue), "Unsuccessful token transfer - arbitrator");
|
||||
if (destinationValue > 0) {
|
||||
require(tokenToPay.transfer(feeDestination, destinationValue), "Unsuccessful token transfer - destination");
|
||||
}
|
||||
} else {
|
||||
_arbitrator.transfer(arbitratorValue);
|
||||
if (destinationValue > 0) {
|
||||
feeDestination.transfer(destinationValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Calculate fee of an amount based in milliPercent
|
||||
* @param _value Value to obtain the fee
|
||||
* @param _milliPercent parameter to calculate the fee
|
||||
* @return Fee amount for _value
|
||||
*/
|
||||
function _getValueOffMillipercent(uint _value, uint _milliPercent) internal pure returns(uint) {
|
||||
// To get the factor, we divide like 100 like a normal percent, but we multiply that by 1000 because it's a milliPercent
|
||||
// Eg: 1 % = 1000 millipercent => Factor is 0.01, so 1000 divided by 100 * 1000
|
||||
return (_value * _milliPercent) / (100 * 1000);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Pay fees for a transaction or element id
|
||||
* This will only transfer funds if the fee has not been paid
|
||||
* @param _from Address from where the fees are being extracted
|
||||
* @param _id Escrow id or element identifier to mark as paid
|
||||
* @param _value Value sold in the escrow
|
||||
* @param _tokenAddress Address of the token sold in the escrow
|
||||
*/
|
||||
function _payFee(address _from, uint _id, uint _value, address _tokenAddress) internal {
|
||||
if (feePaid[_id]) return;
|
||||
|
||||
feePaid[_id] = true;
|
||||
uint feeAmount = _getValueOffMillipercent(_value, feeMilliPercent);
|
||||
feeTokenBalances[_tokenAddress] += feeAmount;
|
||||
|
||||
if (_tokenAddress != address(0)) {
|
||||
require(msg.value == 0, "Cannot send ETH with token address different from 0");
|
||||
|
||||
ERC20Token tokenToPay = ERC20Token(_tokenAddress);
|
||||
require(tokenToPay.transferFrom(_from, address(this), feeAmount + _value), "Unsuccessful token transfer");
|
||||
} else {
|
||||
require(msg.value == (_value + feeAmount), "ETH amount is required");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
pragma solidity >=0.5.0 <0.6.0;
|
||||
|
||||
contract IEscrow {
|
||||
|
||||
enum EscrowStatus {CREATED, FUNDED, PAID, RELEASED, CANCELED}
|
||||
|
||||
struct EscrowTransaction {
|
||||
uint256 offerId;
|
||||
address token;
|
||||
uint256 tokenAmount;
|
||||
uint256 expirationTime;
|
||||
uint256 rating;
|
||||
uint256 assetPrice;
|
||||
address payable buyer;
|
||||
address payable seller;
|
||||
address payable arbitrator;
|
||||
EscrowStatus status;
|
||||
}
|
||||
|
||||
function createEscrow(
|
||||
uint _offerId,
|
||||
uint _tokenAmount,
|
||||
uint _assetPrice,
|
||||
bytes32 _pubkeyA,
|
||||
bytes32 _pubkeyB,
|
||||
string memory _location,
|
||||
string memory _username,
|
||||
uint _nonce,
|
||||
bytes memory _signature
|
||||
) public returns(uint escrowId);
|
||||
|
||||
function pay(uint _escrowId) external;
|
||||
|
||||
function pay_relayed(address _sender, uint _escrowId) external;
|
||||
|
||||
function cancel(uint _escrowId) external;
|
||||
|
||||
function cancel_relayed(address _sender, uint _escrowId) external;
|
||||
|
||||
function openCase(uint _escrowId, string calldata _motive) external;
|
||||
|
||||
function openCase_relayed(address _sender, uint256 _escrowId, string calldata _motive) external;
|
||||
|
||||
function getBasicTradeData(uint _escrowId) external view returns(address payable buyer, address payable seller, address token, uint tokenAmount);
|
||||
|
||||
}
|
|
@ -0,0 +1,152 @@
|
|||
/* solium-disable security/no-block-members */
|
||||
/* solium-disable security/no-inline-assembly */
|
||||
pragma solidity >=0.5.0 <0.6.0;
|
||||
|
||||
import "../common/Ownable.sol";
|
||||
import "../token/ERC20Token.sol";
|
||||
import "../token/ApproveAndCallFallBack.sol";
|
||||
|
||||
/**
|
||||
* @title License
|
||||
* @dev Contract for buying a license
|
||||
*/
|
||||
contract License is Ownable, ApproveAndCallFallBack {
|
||||
uint256 public price;
|
||||
|
||||
ERC20Token token;
|
||||
address burnAddress;
|
||||
|
||||
struct LicenseDetails {
|
||||
uint price;
|
||||
uint creationTime;
|
||||
}
|
||||
|
||||
address[] public licenseOwners;
|
||||
mapping(address => uint) public idxLicenseOwners;
|
||||
mapping(address => LicenseDetails) public licenseDetails;
|
||||
|
||||
event Bought(address buyer, uint256 price);
|
||||
event PriceChanged(uint256 _price);
|
||||
|
||||
bool internal _initialized;
|
||||
|
||||
/**
|
||||
* @param _tokenAddress Address of token used to pay for licenses (SNT)
|
||||
* @param _price Price of the licenses
|
||||
* @param _burnAddress Address where the license fee is going to be sent
|
||||
*/
|
||||
constructor(address _tokenAddress, uint256 _price, address _burnAddress) public {
|
||||
init(_tokenAddress, _price, _burnAddress);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Initialize contract (used with proxy). Can only be called once
|
||||
* @param _tokenAddress Address of token used to pay for licenses (SNT)
|
||||
* @param _price Price of the licenses
|
||||
* @param _burnAddress Address where the license fee is going to be sent
|
||||
*/
|
||||
function init(
|
||||
address _tokenAddress,
|
||||
uint256 _price,
|
||||
address _burnAddress
|
||||
) public {
|
||||
assert(_initialized == false);
|
||||
|
||||
_initialized = true;
|
||||
|
||||
price = _price;
|
||||
token = ERC20Token(_tokenAddress);
|
||||
burnAddress = _burnAddress;
|
||||
|
||||
_setOwner(msg.sender);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Check if the address already owns a license
|
||||
* @param _address The address to check
|
||||
* @return bool
|
||||
*/
|
||||
function isLicenseOwner(address _address) public view returns (bool) {
|
||||
return licenseDetails[_address].price != 0 && licenseDetails[_address].creationTime != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Buy a license
|
||||
* @dev Requires value to be equal to the price of the license.
|
||||
* The msg.sender must not already own a license.
|
||||
*/
|
||||
function buy() external returns(uint) {
|
||||
uint id = _buyFrom(msg.sender);
|
||||
return id;
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Buy a license
|
||||
* @dev Requires value to be equal to the price of the license.
|
||||
* The _owner must not already own a license.
|
||||
*/
|
||||
function _buyFrom(address _licenseOwner) internal returns(uint) {
|
||||
require(licenseDetails[_licenseOwner].creationTime == 0, "License already bought");
|
||||
|
||||
licenseDetails[_licenseOwner] = LicenseDetails({
|
||||
price: price,
|
||||
creationTime: block.timestamp
|
||||
});
|
||||
|
||||
uint idx = licenseOwners.push(_licenseOwner);
|
||||
idxLicenseOwners[_licenseOwner] = idx;
|
||||
|
||||
emit Bought(_licenseOwner, price);
|
||||
|
||||
require(token.transferFrom(_licenseOwner, burnAddress, price), "Unsuccessful token transfer");
|
||||
|
||||
return idx;
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Set the license price
|
||||
* @param _price The new price of the license
|
||||
* @dev Only the owner of the contract can perform this action
|
||||
*/
|
||||
function setPrice(uint256 _price) external onlyOwner {
|
||||
price = _price;
|
||||
emit PriceChanged(_price);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Get number of license owners
|
||||
* @return uint
|
||||
*/
|
||||
function getNumLicenseOwners() external view returns (uint256) {
|
||||
return licenseOwners.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Support for "approveAndCall". Callable only by `token()`.
|
||||
* @param _from Who approved.
|
||||
* @param _amount Amount being approved, need to be equal `price()`.
|
||||
* @param _token Token being approved, need to be equal `token()`.
|
||||
* @param _data Abi encoded data with selector of `buy(and)`.
|
||||
*/
|
||||
function receiveApproval(address _from, uint256 _amount, address _token, bytes memory _data) public {
|
||||
require(_amount == price, "Wrong value");
|
||||
require(_token == address(token), "Wrong token");
|
||||
require(_token == address(msg.sender), "Wrong call");
|
||||
require(_data.length == 4, "Wrong data length");
|
||||
|
||||
require(_abiDecodeBuy(_data) == bytes4(0xa6f2ae3a), "Wrong method selector"); //bytes4(keccak256("buy()"))
|
||||
|
||||
_buyFrom(_from);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Decodes abi encoded data with selector for "buy()".
|
||||
* @param _data Abi encoded data.
|
||||
* @return Decoded registry call.
|
||||
*/
|
||||
function _abiDecodeBuy(bytes memory _data) internal pure returns(bytes4 sig) {
|
||||
assembly {
|
||||
sig := mload(add(_data, add(0x20, 0)))
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,382 @@
|
|||
pragma solidity >=0.5.0 <0.6.0;
|
||||
|
||||
import "./License.sol";
|
||||
import "./ArbitrationLicense.sol";
|
||||
import "../common/MessageSigned.sol";
|
||||
import "../common/Ownable.sol";
|
||||
|
||||
/**
|
||||
* @title MetadataStore
|
||||
* @dev User and offers registry
|
||||
*/
|
||||
contract MetadataStore is MessageSigned {
|
||||
|
||||
struct User {
|
||||
bytes32 pubkeyA;
|
||||
bytes32 pubkeyB;
|
||||
string location;
|
||||
string username;
|
||||
}
|
||||
|
||||
struct Offer {
|
||||
int8 margin;
|
||||
uint[] paymentMethods;
|
||||
uint limitL;
|
||||
uint limitU;
|
||||
address asset;
|
||||
string currency;
|
||||
address payable owner;
|
||||
address payable arbitrator;
|
||||
bool deleted;
|
||||
}
|
||||
|
||||
License public sellingLicenses;
|
||||
ArbitrationLicense public arbitrationLicenses;
|
||||
|
||||
mapping(address => User) public users;
|
||||
mapping(address => uint) public user_nonce;
|
||||
|
||||
Offer[] public offers;
|
||||
mapping(address => uint256[]) public addressToOffers;
|
||||
mapping(address => mapping (uint256 => bool)) public offerWhitelist;
|
||||
|
||||
bool internal _initialized;
|
||||
|
||||
event OfferAdded(
|
||||
address owner,
|
||||
uint256 offerId,
|
||||
address asset,
|
||||
string location,
|
||||
string currency,
|
||||
string username,
|
||||
uint[] paymentMethods,
|
||||
uint limitL,
|
||||
uint limitU,
|
||||
int8 margin
|
||||
);
|
||||
|
||||
event OfferRemoved(address owner, uint256 offerId);
|
||||
|
||||
/**
|
||||
* @param _sellingLicenses Sellers licenses contract address
|
||||
* @param _arbitrationLicenses Arbitrators licenses contract address
|
||||
*/
|
||||
constructor(address _sellingLicenses, address _arbitrationLicenses) public {
|
||||
init(_sellingLicenses, _arbitrationLicenses);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Initialize contract (used with proxy). Can only be called once
|
||||
* @param _sellingLicenses Sellers licenses contract address
|
||||
* @param _arbitrationLicenses Arbitrators licenses contract address
|
||||
*/
|
||||
function init(
|
||||
address _sellingLicenses,
|
||||
address _arbitrationLicenses
|
||||
) public {
|
||||
assert(_initialized == false);
|
||||
|
||||
_initialized = true;
|
||||
|
||||
sellingLicenses = License(_sellingLicenses);
|
||||
arbitrationLicenses = ArbitrationLicense(_arbitrationLicenses);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Get datahash to be signed
|
||||
* @param _username Username
|
||||
* @param _pubkeyA First coordinate of Status Whisper Public Key
|
||||
* @param _pubkeyB Second coordinate of Status Whisper Public Key
|
||||
* @param _nonce Nonce value (obtained from user_nonce)
|
||||
* @return bytes32 to sign
|
||||
*/
|
||||
function _dataHash(string memory _username, bytes32 _pubkeyA, bytes32 _pubkeyB, uint _nonce) internal view returns (bytes32) {
|
||||
return keccak256(abi.encodePacked(address(this), _username, _pubkeyA, _pubkeyB, _nonce));
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Get datahash to be signed
|
||||
* @param _username Username
|
||||
* @param _pubkeyA First coordinate of Status Whisper Public Key
|
||||
* @param _pubkeyB Second coordinate of Status Whisper Public Key
|
||||
* @return bytes32 to sign
|
||||
*/
|
||||
function getDataHash(string calldata _username, bytes32 _pubkeyA, bytes32 _pubkeyB) external view returns (bytes32) {
|
||||
return _dataHash(_username, _pubkeyA, _pubkeyB, user_nonce[msg.sender]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Get signer address from signature. This uses the signature parameters to validate the signature
|
||||
* @param _username Status username
|
||||
* @param _pubkeyA First coordinate of Status Whisper Public Key
|
||||
* @param _pubkeyB Second coordinate of Status Whisper Public Key
|
||||
* @param _nonce User nonce
|
||||
* @param _signature Signature obtained from the previous parameters
|
||||
* @return Signing user address
|
||||
*/
|
||||
function _getSigner(
|
||||
string memory _username,
|
||||
bytes32 _pubkeyA,
|
||||
bytes32 _pubkeyB,
|
||||
uint _nonce,
|
||||
bytes memory _signature
|
||||
) internal view returns(address) {
|
||||
bytes32 signHash = _getSignHash(_dataHash(_username, _pubkeyA, _pubkeyB, _nonce));
|
||||
return _recoverAddress(signHash, _signature);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Get signer address from signature
|
||||
* @param _username Status username
|
||||
* @param _pubkeyA First coordinate of Status Whisper Public Key
|
||||
* @param _pubkeyB Second coordinate of Status Whisper Public Key
|
||||
* @param _nonce User nonce
|
||||
* @param _signature Signature obtained from the previous parameters
|
||||
* @return Signing user address
|
||||
*/
|
||||
function getMessageSigner(
|
||||
string calldata _username,
|
||||
bytes32 _pubkeyA,
|
||||
bytes32 _pubkeyB,
|
||||
uint _nonce,
|
||||
bytes calldata _signature
|
||||
) external view returns(address) {
|
||||
return _getSigner(_username, _pubkeyA, _pubkeyB, _nonce, _signature);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Adds or updates user information
|
||||
* @param _user User address to update
|
||||
* @param _pubkeyA First coordinate of Status Whisper Public Key
|
||||
* @param _pubkeyB Second coordinate of Status Whisper Public Key
|
||||
* @param _location New location
|
||||
* @param _username New status username
|
||||
*/
|
||||
function _addOrUpdateUser(
|
||||
address _user,
|
||||
bytes32 _pubkeyA,
|
||||
bytes32 _pubkeyB,
|
||||
string memory _location,
|
||||
string memory _username
|
||||
) internal {
|
||||
User storage u = users[_user];
|
||||
u.pubkeyA = _pubkeyA;
|
||||
u.pubkeyB = _pubkeyB;
|
||||
u.location = _location;
|
||||
u.username = _username;
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Adds or updates user information via signature
|
||||
* @param _signature Signature
|
||||
* @param _pubkeyA First coordinate of Status Whisper Public Key
|
||||
* @param _pubkeyB Second coordinate of Status Whisper Public Key
|
||||
* @param _location New location
|
||||
* @param _username New status username
|
||||
* @return Signing user address
|
||||
*/
|
||||
function addOrUpdateUser(
|
||||
bytes calldata _signature,
|
||||
bytes32 _pubkeyA,
|
||||
bytes32 _pubkeyB,
|
||||
string calldata _location,
|
||||
string calldata _username,
|
||||
uint _nonce
|
||||
) external returns(address payable _user) {
|
||||
_user = address(uint160(_getSigner(_username, _pubkeyA, _pubkeyB, _nonce, _signature)));
|
||||
|
||||
require(_nonce == user_nonce[_user], "Invalid nonce");
|
||||
|
||||
user_nonce[_user]++;
|
||||
_addOrUpdateUser(_user, _pubkeyA, _pubkeyB, _location, _username);
|
||||
|
||||
return _user;
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Adds or updates user information
|
||||
* @param _pubkeyA First coordinate of Status Whisper Public Key
|
||||
* @param _pubkeyB Second coordinate of Status Whisper Public Key
|
||||
* @param _location New location
|
||||
* @param _username New status username
|
||||
* @return Signing user address
|
||||
*/
|
||||
function addOrUpdateUser(
|
||||
bytes32 _pubkeyA,
|
||||
bytes32 _pubkeyB,
|
||||
string calldata _location,
|
||||
string calldata _username
|
||||
) external {
|
||||
_addOrUpdateUser(msg.sender, _pubkeyA, _pubkeyB, _location, _username);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Add a new offer with a new user if needed to the list
|
||||
* @param _asset The address of the erc20 to exchange, pass 0x0 for Eth
|
||||
* @param _pubkeyA First coordinate of Status Whisper Public Key
|
||||
* @param _pubkeyB Second coordinate of Status Whisper Public Key
|
||||
* @param _location The location on earth
|
||||
* @param _currency The currency the user want to receive (USD, EUR...)
|
||||
* @param _username The username of the user
|
||||
* @param _paymentMethods The list of the payment methods the user accept
|
||||
* @param _limitL Lower limit accepted
|
||||
* @param _limitU Upper limit accepted
|
||||
* @param _margin The margin for the user from 0 to 100
|
||||
* @param _arbitrator The arbitrator used by the offer
|
||||
*/
|
||||
function addOffer(
|
||||
address _asset,
|
||||
bytes32 _pubkeyA,
|
||||
bytes32 _pubkeyB,
|
||||
string memory _location,
|
||||
string memory _currency,
|
||||
string memory _username,
|
||||
uint[] memory _paymentMethods,
|
||||
uint _limitL,
|
||||
uint _limitU,
|
||||
int8 _margin,
|
||||
address payable _arbitrator
|
||||
) public {
|
||||
require(sellingLicenses.isLicenseOwner(msg.sender), "Not a license owner");
|
||||
require(arbitrationLicenses.isAllowed(msg.sender, _arbitrator), "Arbitrator does not allow this transaction");
|
||||
|
||||
require(_margin <= 100, "Margin too high");
|
||||
require(_margin >= -100, "Margin too low");
|
||||
require(_limitL <= _limitU, "Invalid limits");
|
||||
require(msg.sender != _arbitrator, "Cannot arbitrate own offers");
|
||||
|
||||
_addOrUpdateUser(
|
||||
msg.sender,
|
||||
_pubkeyA,
|
||||
_pubkeyB,
|
||||
_location,
|
||||
_username
|
||||
);
|
||||
|
||||
Offer memory newOffer = Offer(
|
||||
_margin,
|
||||
_paymentMethods,
|
||||
_limitL,
|
||||
_limitU,
|
||||
_asset,
|
||||
_currency,
|
||||
msg.sender,
|
||||
_arbitrator,
|
||||
false
|
||||
);
|
||||
|
||||
uint256 offerId = offers.push(newOffer) - 1;
|
||||
offerWhitelist[msg.sender][offerId] = true;
|
||||
addressToOffers[msg.sender].push(offerId);
|
||||
|
||||
emit OfferAdded(
|
||||
msg.sender,
|
||||
offerId,
|
||||
_asset,
|
||||
_location,
|
||||
_currency,
|
||||
_username,
|
||||
_paymentMethods,
|
||||
_limitL,
|
||||
_limitU,
|
||||
_margin);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Remove user offer
|
||||
* @dev Removed offers are marked as deleted instead of being deleted
|
||||
* @param _offerId Id of the offer to remove
|
||||
*/
|
||||
function removeOffer(uint256 _offerId) external {
|
||||
require(offerWhitelist[msg.sender][_offerId], "Offer does not exist");
|
||||
|
||||
offers[_offerId].deleted = true;
|
||||
offerWhitelist[msg.sender][_offerId] = false;
|
||||
emit OfferRemoved(msg.sender, _offerId);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Get the offer by Id
|
||||
* @dev normally we'd access the offers array, but it would not return the payment methods
|
||||
* @param _id Offer id
|
||||
* @return Offer data (see Offer struct)
|
||||
*/
|
||||
function offer(uint256 _id) external view returns (
|
||||
address asset,
|
||||
string memory currency,
|
||||
int8 margin,
|
||||
uint[] memory paymentMethods,
|
||||
uint limitL,
|
||||
uint limitH,
|
||||
address payable owner,
|
||||
address payable arbitrator,
|
||||
bool deleted
|
||||
) {
|
||||
Offer memory theOffer = offers[_id];
|
||||
|
||||
// In case arbitrator rejects the seller
|
||||
address payable offerArbitrator = theOffer.arbitrator;
|
||||
if(!arbitrationLicenses.isAllowed(theOffer.owner, offerArbitrator)){
|
||||
offerArbitrator = address(0);
|
||||
}
|
||||
|
||||
return (
|
||||
theOffer.asset,
|
||||
theOffer.currency,
|
||||
theOffer.margin,
|
||||
theOffer.paymentMethods,
|
||||
theOffer.limitL,
|
||||
theOffer.limitU,
|
||||
theOffer.owner,
|
||||
offerArbitrator,
|
||||
theOffer.deleted
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Get the offer's owner by Id
|
||||
* @dev Helper function
|
||||
* @param _id Offer id
|
||||
* @return Seller address
|
||||
*/
|
||||
function getOfferOwner(uint256 _id) external view returns (address payable) {
|
||||
return (offers[_id].owner);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Get the offer's asset by Id
|
||||
* @dev Helper function
|
||||
* @param _id Offer id
|
||||
* @return Token address used in the offer
|
||||
*/
|
||||
function getAsset(uint256 _id) external view returns (address) {
|
||||
return (offers[_id].asset);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Get the offer's arbitrator by Id
|
||||
* @dev Helper function
|
||||
* @param _id Offer id
|
||||
* @return Arbitrator address
|
||||
*/
|
||||
function getArbitrator(uint256 _id) external view returns (address payable) {
|
||||
return (offers[_id].arbitrator);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Get the size of the offers
|
||||
* @return Number of offers stored in the contract
|
||||
*/
|
||||
function offersSize() external view returns (uint256) {
|
||||
return offers.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Get all the offer ids of the address in params
|
||||
* @param _address Address of the offers
|
||||
* @return Array of offer ids for a specific address
|
||||
*/
|
||||
function getOfferIds(address _address) external view returns (uint256[] memory) {
|
||||
return addressToOffers[_address];
|
||||
}
|
||||
}
|
|
@ -0,0 +1,149 @@
|
|||
pragma solidity ^0.5.7;
|
||||
|
||||
import "../token/ERC20Token.sol";
|
||||
|
||||
|
||||
/**
|
||||
* @notice Standard ERC20 token for tests
|
||||
*/
|
||||
contract StandardToken is ERC20Token {
|
||||
|
||||
uint256 private supply;
|
||||
mapping (address => uint256) balances;
|
||||
mapping (address => mapping (address => uint256)) allowed;
|
||||
|
||||
constructor() public { }
|
||||
|
||||
/**
|
||||
* @notice send `_value` token to `_to` from `msg.sender`
|
||||
* @param _to The address of the recipient
|
||||
* @param _value The amount of token to be transferred
|
||||
* @return Whether the transfer was successful or not
|
||||
*/
|
||||
function transfer(
|
||||
address _to,
|
||||
uint256 _value
|
||||
)
|
||||
external
|
||||
returns (bool success)
|
||||
{
|
||||
return transfer(msg.sender, _to, _value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice `msg.sender` approves `_spender` to spend `_value` tokens
|
||||
* @param _spender The address of the account able to transfer the tokens
|
||||
* @param _value The amount of tokens to be approved for transfer
|
||||
* @return Whether the approval was successful or not
|
||||
*/
|
||||
function approve(address _spender, uint256 _value)
|
||||
external
|
||||
returns (bool success)
|
||||
{
|
||||
allowed[msg.sender][_spender] = _value;
|
||||
emit Approval(msg.sender, _spender, _value);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice send `_value` token to `_to` from `_from` on the condition it is approved by `_from`
|
||||
* @param _from The address of the sender
|
||||
* @param _to The address of the recipient
|
||||
* @param _value The amount of token to be transferred
|
||||
* @return Whether the transfer was successful or not
|
||||
*/
|
||||
function transferFrom(
|
||||
address _from,
|
||||
address _to,
|
||||
uint256 _value
|
||||
)
|
||||
external
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param _owner The address from which the balance will be retrieved
|
||||
* @param _spender The address of the account able to spend the tokens
|
||||
* @return The balance
|
||||
*/
|
||||
function allowance(address _owner, address _spender)
|
||||
external
|
||||
view
|
||||
returns (uint256 remaining)
|
||||
{
|
||||
return allowed[_owner][_spender];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param _owner The address of the account owning tokens
|
||||
* @return Amount of remaining tokens allowed to spent
|
||||
*/
|
||||
function balanceOf(address _owner)
|
||||
external
|
||||
view
|
||||
returns (uint256 balance)
|
||||
{
|
||||
return balances[_owner];
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice return total supply of tokens
|
||||
*/
|
||||
function totalSupply()
|
||||
external
|
||||
view
|
||||
returns(uint256 currentTotalSupply)
|
||||
{
|
||||
return supply;
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Mints tokens for testing
|
||||
*/
|
||||
function mint(
|
||||
address _to,
|
||||
uint256 _amount
|
||||
)
|
||||
public
|
||||
{
|
||||
balances[_to] += _amount;
|
||||
supply += _amount;
|
||||
emit Transfer(address(0), _to, _amount);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice send `_value` token to `_to` from `_from`
|
||||
* @param _from The address of the sender
|
||||
* @param _to The address of the recipient
|
||||
* @param _value The amount of token to be transferred
|
||||
* @return Whether the transfer was successful or not
|
||||
*/
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
/* solium-disable security/no-block-members */
|
||||
/* solium-disable no-empty-blocks */
|
||||
|
||||
pragma solidity >=0.5.0 <0.6.0;
|
||||
|
||||
import "../teller-network/Escrow.sol";
|
||||
|
||||
/**
|
||||
* @title Test Escrow Upgrade contract
|
||||
*/
|
||||
contract TestEscrowUpgrade is Escrow {
|
||||
|
||||
constructor (
|
||||
address _relayer,
|
||||
address _license,
|
||||
address _arbitrationLicense,
|
||||
address _metadataStore,
|
||||
address payable _feeDestination,
|
||||
uint _feeMilliPercent
|
||||
)
|
||||
Escrow(_relayer, _license, _arbitrationLicense, _metadataStore, _feeDestination, _feeMilliPercent)
|
||||
public {
|
||||
}
|
||||
|
||||
uint private val;
|
||||
|
||||
function getVal() public view returns (uint) {
|
||||
return val;
|
||||
}
|
||||
|
||||
function setVal(uint _val) public {
|
||||
val = _val;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
pragma solidity ^0.5.7;
|
||||
|
||||
contract ApproveAndCallFallBack {
|
||||
function receiveApproval(address from, uint256 _amount, address _token, bytes memory _data) public;
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
pragma solidity ^0.5.7;
|
||||
|
||||
// Abstract contract for the full ERC 20 Token standard
|
||||
// https://github.com/ethereum/EIPs/issues/20
|
||||
|
||||
interface ERC20Token {
|
||||
|
||||
/**
|
||||
* @notice send `_value` token to `_to` from `msg.sender`
|
||||
* @param _to The address of the recipient
|
||||
* @param _value The amount of token to be transferred
|
||||
* @return Whether the transfer was successful or not
|
||||
*/
|
||||
function transfer(address _to, uint256 _value) external returns (bool success);
|
||||
|
||||
/**
|
||||
* @notice `msg.sender` approves `_spender` to spend `_value` tokens
|
||||
* @param _spender The address of the account able to transfer the tokens
|
||||
* @param _value The amount of tokens to be approved for transfer
|
||||
* @return Whether the approval was successful or not
|
||||
*/
|
||||
function approve(address _spender, uint256 _value) external returns (bool success);
|
||||
|
||||
/**
|
||||
* @notice send `_value` token to `_to` from `_from` on the condition it is approved by `_from`
|
||||
* @param _from The address of the sender
|
||||
* @param _to The address of the recipient
|
||||
* @param _value The amount of token to be transferred
|
||||
* @return Whether the transfer was successful or not
|
||||
*/
|
||||
function transferFrom(address _from, address _to, uint256 _value) external returns (bool success);
|
||||
|
||||
/**
|
||||
* @param _owner The address from which the balance will be retrieved
|
||||
* @return The balance
|
||||
*/
|
||||
function balanceOf(address _owner) external view returns (uint256 balance);
|
||||
|
||||
/**
|
||||
* @param _owner The address of the account owning tokens
|
||||
* @param _spender The address of the account able to transfer the tokens
|
||||
* @return Amount of remaining tokens allowed to spent
|
||||
*/
|
||||
function allowance(address _owner, address _spender) external view returns (uint256 remaining);
|
||||
|
||||
/**
|
||||
* @notice return total supply of tokens
|
||||
*/
|
||||
function totalSupply() external view returns (uint256 supply);
|
||||
|
||||
event Transfer(address indexed _from, address indexed _to, uint256 _value);
|
||||
event Approval(address indexed _owner, address indexed _spender, uint256 _value);
|
||||
}
|
|
@ -0,0 +1,637 @@
|
|||
pragma solidity ^0.5.7;
|
||||
|
||||
/*
|
||||
Copyright 2016, Jordi Baylina
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/**
|
||||
* @title MiniMeToken Contract
|
||||
* @author Jordi Baylina
|
||||
* @dev This token contract's goal is to make it easy for anyone to clone this
|
||||
* token using the token distribution at a given block, this will allow DAO's
|
||||
* and DApps to upgrade their features in a decentralized manner without
|
||||
* affecting the original token
|
||||
* @dev It is ERC20 compliant, but still needs to under go further testing.
|
||||
*/
|
||||
|
||||
import "../common/Controlled.sol";
|
||||
import "./TokenController.sol";
|
||||
import "./ApproveAndCallFallBack.sol";
|
||||
import "./MiniMeTokenInterface.sol";
|
||||
import "./TokenFactory.sol";
|
||||
|
||||
/**
|
||||
* @dev The actual token contract, the default controller is the msg.sender
|
||||
* that deploys the contract, so usually this token will be deployed by a
|
||||
* token controller contract, which Giveth will call a "Campaign"
|
||||
*/
|
||||
contract MiniMeToken is MiniMeTokenInterface, Controlled {
|
||||
|
||||
string public name; //The Token's name: e.g. DigixDAO Tokens
|
||||
uint8 public decimals; //Number of decimals of the smallest unit
|
||||
string public symbol; //An identifier: e.g. REP
|
||||
string public version = "MMT_0.1"; //An arbitrary versioning scheme
|
||||
|
||||
/**
|
||||
* @dev `Checkpoint` is the structure that attaches a block number to a
|
||||
* given value, the block number attached is the one that last changed the
|
||||
* value
|
||||
*/
|
||||
struct Checkpoint {
|
||||
|
||||
// `fromBlock` is the block number that the value was generated from
|
||||
uint128 fromBlock;
|
||||
|
||||
// `value` is the amount of tokens at a specific block number
|
||||
uint128 value;
|
||||
}
|
||||
|
||||
// `parentToken` is the Token address that was cloned to produce this token;
|
||||
// it will be 0x0 for a token that was not cloned
|
||||
MiniMeToken public parentToken;
|
||||
|
||||
// `parentSnapShotBlock` is the block number from the Parent Token that was
|
||||
// used to determine the initial distribution of the Clone Token
|
||||
uint public parentSnapShotBlock;
|
||||
|
||||
// `creationBlock` is the block number that the Clone Token was created
|
||||
uint public creationBlock;
|
||||
|
||||
// `balances` is the map that tracks the balance of each address, in this
|
||||
// contract when the balance changes the block number that the change
|
||||
// occurred is also included in the map
|
||||
mapping (address => Checkpoint[]) balances;
|
||||
|
||||
// `allowed` tracks any extra transfer rights as in all ERC20 tokens
|
||||
mapping (address => mapping (address => uint256)) allowed;
|
||||
|
||||
// Tracks the history of the `totalSupply` of the token
|
||||
Checkpoint[] totalSupplyHistory;
|
||||
|
||||
// Flag that determines if the token is transferable or not.
|
||||
bool public transfersEnabled;
|
||||
|
||||
// The factory used to create new clone tokens
|
||||
TokenFactory public tokenFactory;
|
||||
|
||||
////////////////
|
||||
// Constructor
|
||||
////////////////
|
||||
|
||||
/**
|
||||
* @notice Constructor to create a MiniMeToken
|
||||
* @param _tokenFactory The address of the MiniMeTokenFactory contract that
|
||||
* will create the Clone token contracts, the token factory needs to be
|
||||
* deployed first
|
||||
* @param _parentToken Address of the parent token, set to 0x0 if it is a
|
||||
* new token
|
||||
* @param _parentSnapShotBlock Block of the parent token that will
|
||||
* determine the initial distribution of the clone token, set to 0 if it
|
||||
* is a new token
|
||||
* @param _tokenName Name of the new token
|
||||
* @param _decimalUnits Number of decimals of the new token
|
||||
* @param _tokenSymbol Token Symbol for the new token
|
||||
* @param _transfersEnabled If true, tokens will be able to be transferred
|
||||
*/
|
||||
constructor(
|
||||
address _tokenFactory,
|
||||
address _parentToken,
|
||||
uint _parentSnapShotBlock,
|
||||
string memory _tokenName,
|
||||
uint8 _decimalUnits,
|
||||
string memory _tokenSymbol,
|
||||
bool _transfersEnabled
|
||||
)
|
||||
public
|
||||
{
|
||||
tokenFactory = TokenFactory(_tokenFactory);
|
||||
name = _tokenName; // Set the name
|
||||
decimals = _decimalUnits; // Set the decimals
|
||||
symbol = _tokenSymbol; // Set the symbol
|
||||
parentToken = MiniMeToken(address(uint160(_parentToken)));
|
||||
parentSnapShotBlock = _parentSnapShotBlock;
|
||||
transfersEnabled = _transfersEnabled;
|
||||
creationBlock = block.number;
|
||||
}
|
||||
|
||||
|
||||
///////////////////
|
||||
// ERC20 Methods
|
||||
///////////////////
|
||||
|
||||
/**
|
||||
* @notice Send `_amount` tokens to `_to` from `msg.sender`
|
||||
* @param _to The address of the recipient
|
||||
* @param _amount The amount of tokens to be transferred
|
||||
* @return Whether the transfer was successful or not
|
||||
*/
|
||||
function transfer(address _to, uint256 _amount) public returns (bool success) {
|
||||
require(transfersEnabled);
|
||||
return doTransfer(msg.sender, _to, _amount);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Send `_amount` tokens to `_to` from `_from` on the condition it
|
||||
* is approved by `_from`
|
||||
* @param _from The address holding the tokens being transferred
|
||||
* @param _to The address of the recipient
|
||||
* @param _amount The amount of tokens to be transferred
|
||||
* @return True if the transfer was successful
|
||||
*/
|
||||
function transferFrom(
|
||||
address _from,
|
||||
address _to,
|
||||
uint256 _amount
|
||||
)
|
||||
public
|
||||
returns (bool success)
|
||||
{
|
||||
|
||||
// The controller of this contract can move tokens around at will,
|
||||
// this is important to recognize! Confirm that you trust the
|
||||
// controller of this contract, which in most situations should be
|
||||
// another open source smart contract or 0x0
|
||||
if (msg.sender != controller) {
|
||||
require(transfersEnabled);
|
||||
|
||||
// The standard ERC 20 transferFrom functionality
|
||||
if (allowed[_from][msg.sender] < _amount) {
|
||||
return false;
|
||||
}
|
||||
allowed[_from][msg.sender] -= _amount;
|
||||
}
|
||||
return doTransfer(_from, _to, _amount);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev This is the actual transfer function in the token contract, it can
|
||||
* only be called by other functions in this contract.
|
||||
* @param _from The address holding the tokens being transferred
|
||||
* @param _to The address of the recipient
|
||||
* @param _amount The amount of tokens to be transferred
|
||||
* @return True if the transfer was successful
|
||||
*/
|
||||
function doTransfer(
|
||||
address _from,
|
||||
address _to,
|
||||
uint _amount
|
||||
)
|
||||
internal
|
||||
returns(bool)
|
||||
{
|
||||
|
||||
if (_amount == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
require(parentSnapShotBlock < block.number);
|
||||
|
||||
// Do not allow transfer to 0x0 or the token contract itself
|
||||
require((_to != address(0)) && (_to != address(this)));
|
||||
|
||||
// If the amount being transfered is more than the balance of the
|
||||
// account the transfer returns false
|
||||
uint256 previousBalanceFrom = balanceOfAt(_from, block.number);
|
||||
if (previousBalanceFrom < _amount) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Alerts the token controller of the transfer
|
||||
if (isContract(controller)) {
|
||||
require(TokenController(controller).onTransfer(_from, _to, _amount));
|
||||
}
|
||||
|
||||
// First update the balance array with the new value for the address
|
||||
// sending the tokens
|
||||
updateValueAtNow(balances[_from], previousBalanceFrom - _amount);
|
||||
|
||||
// Then update the balance array with the new value for the address
|
||||
// receiving the tokens
|
||||
uint256 previousBalanceTo = balanceOfAt(_to, block.number);
|
||||
require(previousBalanceTo + _amount >= previousBalanceTo); // Check for overflow
|
||||
updateValueAtNow(balances[_to], previousBalanceTo + _amount);
|
||||
|
||||
// An event to make the transfer easy to find on the blockchain
|
||||
emit Transfer(_from, _to, _amount);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function doApprove(
|
||||
address _from,
|
||||
address _spender,
|
||||
uint256 _amount
|
||||
)
|
||||
internal
|
||||
returns (bool)
|
||||
{
|
||||
require(transfersEnabled);
|
||||
|
||||
// To change the approve amount you first have to reduce the addresses`
|
||||
// allowance to zero by calling `approve(_spender,0)` if it is not
|
||||
// already 0 to mitigate the race condition described here:
|
||||
// https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
|
||||
require((_amount == 0) || (allowed[_from][_spender] == 0));
|
||||
|
||||
// Alerts the token controller of the approve function call
|
||||
if (isContract(controller)) {
|
||||
require(TokenController(controller).onApprove(_from, _spender, _amount));
|
||||
}
|
||||
|
||||
allowed[_from][_spender] = _amount;
|
||||
emit Approval(_from, _spender, _amount);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param _owner The address that's balance is being requested
|
||||
* @return The balance of `_owner` at the current block
|
||||
*/
|
||||
function balanceOf(address _owner) external view returns (uint256 balance) {
|
||||
return balanceOfAt(_owner, block.number);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice `msg.sender` approves `_spender` to spend `_amount` tokens on
|
||||
* its behalf. This is a modified version of the ERC20 approve function
|
||||
* to be a little bit safer
|
||||
* @param _spender The address of the account able to transfer the tokens
|
||||
* @param _amount The amount of tokens to be approved for transfer
|
||||
* @return True if the approval was successful
|
||||
*/
|
||||
function approve(address _spender, uint256 _amount) external returns (bool success) {
|
||||
return doApprove(msg.sender, _spender, _amount);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev This function makes it easy to read the `allowed[]` map
|
||||
* @param _owner The address of the account that owns the token
|
||||
* @param _spender The address of the account able to transfer the tokens
|
||||
* @return Amount of remaining tokens of _owner that _spender is allowed
|
||||
* to spend
|
||||
*/
|
||||
function allowance(
|
||||
address _owner,
|
||||
address _spender
|
||||
)
|
||||
external
|
||||
view
|
||||
returns (uint256 remaining)
|
||||
{
|
||||
return allowed[_owner][_spender];
|
||||
}
|
||||
/**
|
||||
* @notice `msg.sender` approves `_spender` to send `_amount` tokens on
|
||||
* its behalf, and then a function is triggered in the contract that is
|
||||
* being approved, `_spender`. This allows users to use their tokens to
|
||||
* interact with contracts in one function call instead of two
|
||||
* @param _spender The address of the contract able to transfer the tokens
|
||||
* @param _amount The amount of tokens to be approved for transfer
|
||||
* @return True if the function call was successful
|
||||
*/
|
||||
function approveAndCall(
|
||||
address _spender,
|
||||
uint256 _amount,
|
||||
bytes memory _extraData
|
||||
)
|
||||
public
|
||||
returns (bool success)
|
||||
{
|
||||
require(doApprove(msg.sender, _spender, _amount));
|
||||
|
||||
ApproveAndCallFallBack(_spender).receiveApproval(
|
||||
msg.sender,
|
||||
_amount,
|
||||
address(this),
|
||||
_extraData
|
||||
);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev This function makes it easy to get the total number of tokens
|
||||
* @return The total number of tokens
|
||||
*/
|
||||
function totalSupply() external view returns (uint) {
|
||||
return totalSupplyAt(block.number);
|
||||
}
|
||||
|
||||
|
||||
////////////////
|
||||
// Query balance and totalSupply in History
|
||||
////////////////
|
||||
|
||||
/**
|
||||
* @dev Queries the balance of `_owner` at a specific `_blockNumber`
|
||||
* @param _owner The address from which the balance will be retrieved
|
||||
* @param _blockNumber The block number when the balance is queried
|
||||
* @return The balance at `_blockNumber`
|
||||
*/
|
||||
function balanceOfAt(
|
||||
address _owner,
|
||||
uint _blockNumber
|
||||
)
|
||||
public
|
||||
view
|
||||
returns (uint)
|
||||
{
|
||||
|
||||
// These next few lines are used when the balance of the token is
|
||||
// requested before a check point was ever created for this token, it
|
||||
// requires that the `parentToken.balanceOfAt` be queried at the
|
||||
// genesis block for that token as this contains initial balance of
|
||||
// this token
|
||||
if ((balances[_owner].length == 0)
|
||||
|| (balances[_owner][0].fromBlock > _blockNumber)) {
|
||||
if (address(parentToken) != address(0)) {
|
||||
return parentToken.balanceOfAt(_owner, min(_blockNumber, parentSnapShotBlock));
|
||||
} else {
|
||||
// Has no parent
|
||||
return 0;
|
||||
}
|
||||
|
||||
// This will return the expected balance during normal situations
|
||||
} else {
|
||||
return getValueAt(balances[_owner], _blockNumber);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Total amount of tokens at a specific `_blockNumber`.
|
||||
* @param _blockNumber The block number when the totalSupply is queried
|
||||
* @return The total amount of tokens at `_blockNumber`
|
||||
*/
|
||||
function totalSupplyAt(uint _blockNumber) public view returns(uint) {
|
||||
|
||||
// These next few lines are used when the totalSupply of the token is
|
||||
// requested before a check point was ever created for this token, it
|
||||
// requires that the `parentToken.totalSupplyAt` be queried at the
|
||||
// genesis block for this token as that contains totalSupply of this
|
||||
// token at this block number.
|
||||
if ((totalSupplyHistory.length == 0)
|
||||
|| (totalSupplyHistory[0].fromBlock > _blockNumber)) {
|
||||
if (address(parentToken) != address(0)) {
|
||||
return parentToken.totalSupplyAt(min(_blockNumber, parentSnapShotBlock));
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// This will return the expected totalSupply during normal situations
|
||||
} else {
|
||||
return getValueAt(totalSupplyHistory, _blockNumber);
|
||||
}
|
||||
}
|
||||
|
||||
////////////////
|
||||
// Clone Token Method
|
||||
////////////////
|
||||
|
||||
/**
|
||||
* @notice Creates a new clone token with the initial distribution being
|
||||
* this token at `snapshotBlock`
|
||||
* @param _cloneTokenName Name of the clone token
|
||||
* @param _cloneDecimalUnits Number of decimals of the smallest unit
|
||||
* @param _cloneTokenSymbol Symbol of the clone token
|
||||
* @param _snapshotBlock Block when the distribution of the parent token is
|
||||
* copied to set the initial distribution of the new clone token;
|
||||
* if the block is zero than the actual block, the current block is used
|
||||
* @param _transfersEnabled True if transfers are allowed in the clone
|
||||
* @return The address of the new MiniMeToken Contract
|
||||
*/
|
||||
function createCloneToken(
|
||||
string memory _cloneTokenName,
|
||||
uint8 _cloneDecimalUnits,
|
||||
string memory _cloneTokenSymbol,
|
||||
uint _snapshotBlock,
|
||||
bool _transfersEnabled
|
||||
)
|
||||
public
|
||||
returns(address)
|
||||
{
|
||||
uint snapshotBlock = _snapshotBlock;
|
||||
if (snapshotBlock == 0) {
|
||||
snapshotBlock = block.number;
|
||||
}
|
||||
MiniMeToken cloneToken = MiniMeToken(tokenFactory.createCloneToken(
|
||||
address(this),
|
||||
snapshotBlock,
|
||||
_cloneTokenName,
|
||||
_cloneDecimalUnits,
|
||||
_cloneTokenSymbol,
|
||||
_transfersEnabled
|
||||
));
|
||||
|
||||
cloneToken.changeController(msg.sender);
|
||||
|
||||
// An event to make the token easy to find on the blockchain
|
||||
emit NewCloneToken(address(cloneToken), snapshotBlock);
|
||||
return address(cloneToken);
|
||||
}
|
||||
|
||||
////////////////
|
||||
// Generate and destroy tokens
|
||||
////////////////
|
||||
|
||||
/**
|
||||
* @notice Generates `_amount` tokens that are assigned to `_owner`
|
||||
* @param _owner The address that will be assigned the new tokens
|
||||
* @param _amount The quantity of tokens generated
|
||||
* @return True if the tokens are generated correctly
|
||||
*/
|
||||
function generateTokens(
|
||||
address _owner,
|
||||
uint _amount
|
||||
)
|
||||
public
|
||||
onlyController
|
||||
returns (bool)
|
||||
{
|
||||
uint curTotalSupply = totalSupplyAt(block.number);
|
||||
require(curTotalSupply + _amount >= curTotalSupply); // Check for overflow
|
||||
uint previousBalanceTo = balanceOfAt(_owner, block.number);
|
||||
require(previousBalanceTo + _amount >= previousBalanceTo); // Check for overflow
|
||||
updateValueAtNow(totalSupplyHistory, curTotalSupply + _amount);
|
||||
updateValueAtNow(balances[_owner], previousBalanceTo + _amount);
|
||||
emit Transfer(address(0), _owner, _amount);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Burns `_amount` tokens from `_owner`
|
||||
* @param _owner The address that will lose the tokens
|
||||
* @param _amount The quantity of tokens to burn
|
||||
* @return True if the tokens are burned correctly
|
||||
*/
|
||||
function destroyTokens(
|
||||
address _owner,
|
||||
uint _amount
|
||||
)
|
||||
public
|
||||
onlyController
|
||||
returns (bool)
|
||||
{
|
||||
uint curTotalSupply = totalSupplyAt(block.number);
|
||||
require(curTotalSupply >= _amount);
|
||||
uint previousBalanceFrom = balanceOfAt(_owner, block.number);
|
||||
require(previousBalanceFrom >= _amount);
|
||||
updateValueAtNow(totalSupplyHistory, curTotalSupply - _amount);
|
||||
updateValueAtNow(balances[_owner], previousBalanceFrom - _amount);
|
||||
emit Transfer(_owner, address(0), _amount);
|
||||
return true;
|
||||
}
|
||||
|
||||
////////////////
|
||||
// Enable tokens transfers
|
||||
////////////////
|
||||
|
||||
/**
|
||||
* @notice Enables token holders to transfer their tokens freely if true
|
||||
* @param _transfersEnabled True if transfers are allowed in the clone
|
||||
*/
|
||||
function enableTransfers(bool _transfersEnabled) public onlyController {
|
||||
transfersEnabled = _transfersEnabled;
|
||||
}
|
||||
|
||||
////////////////
|
||||
// Internal helper functions to query and set a value in a snapshot array
|
||||
////////////////
|
||||
|
||||
/**
|
||||
* @dev `getValueAt` retrieves the number of tokens at a given block number
|
||||
* @param checkpoints The history of values being queried
|
||||
* @param _block The block number to retrieve the value at
|
||||
* @return The number of tokens being queried
|
||||
*/
|
||||
function getValueAt(
|
||||
Checkpoint[] storage checkpoints,
|
||||
uint _block
|
||||
)
|
||||
internal
|
||||
view
|
||||
returns (uint)
|
||||
{
|
||||
if (checkpoints.length == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Shortcut for the actual value
|
||||
if (_block >= checkpoints[checkpoints.length-1].fromBlock) {
|
||||
return checkpoints[checkpoints.length-1].value;
|
||||
}
|
||||
if (_block < checkpoints[0].fromBlock) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Binary search of the value in the array
|
||||
uint min = 0;
|
||||
uint max = checkpoints.length-1;
|
||||
while (max > min) {
|
||||
uint mid = (max + min + 1) / 2;
|
||||
if (checkpoints[mid].fromBlock<=_block) {
|
||||
min = mid;
|
||||
} else {
|
||||
max = mid-1;
|
||||
}
|
||||
}
|
||||
return checkpoints[min].value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev `updateValueAtNow` used to update the `balances` map and the
|
||||
* `totalSupplyHistory`
|
||||
* @param checkpoints The history of data being updated
|
||||
* @param _value The new number of tokens
|
||||
*/
|
||||
function updateValueAtNow(Checkpoint[] storage checkpoints, uint _value) internal {
|
||||
if ((checkpoints.length == 0)
|
||||
|| (checkpoints[checkpoints.length -1].fromBlock < block.number)) {
|
||||
Checkpoint storage newCheckPoint = checkpoints[checkpoints.length++];
|
||||
newCheckPoint.fromBlock = uint128(block.number);
|
||||
newCheckPoint.value = uint128(_value);
|
||||
} else {
|
||||
Checkpoint storage oldCheckPoint = checkpoints[checkpoints.length-1];
|
||||
oldCheckPoint.value = uint128(_value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Internal function to determine if an address is a contract
|
||||
* @param _addr The address being queried
|
||||
* @return True if `_addr` is a contract
|
||||
*/
|
||||
function isContract(address _addr) internal view returns(bool) {
|
||||
uint size;
|
||||
if (_addr == address(0)){
|
||||
return false;
|
||||
}
|
||||
assembly {
|
||||
size := extcodesize(_addr)
|
||||
}
|
||||
return size>0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Helper function to return a min betwen the two uints
|
||||
*/
|
||||
function min(uint a, uint b) internal pure returns (uint) {
|
||||
return a < b ? a : b;
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice The fallback function: If the contract's controller has not been
|
||||
* set to 0, then the `proxyPayment` method is called which relays the
|
||||
* ether and creates tokens as described in the token controller contract
|
||||
*/
|
||||
function () external payable {
|
||||
require(isContract(controller));
|
||||
require(TokenController(controller).proxyPayment.value(msg.value)(msg.sender));
|
||||
}
|
||||
|
||||
//////////
|
||||
// Safety Methods
|
||||
//////////
|
||||
|
||||
/**
|
||||
* @notice This method can be used by the controller to extract mistakenly
|
||||
* sent tokens to this contract.
|
||||
* @param _token The address of the token contract that you want to recover
|
||||
* set to 0 in case you want to extract ether.
|
||||
*/
|
||||
function claimTokens(address _token) public onlyController {
|
||||
if (_token == address(0)) {
|
||||
controller.transfer(address(this).balance);
|
||||
return;
|
||||
}
|
||||
|
||||
MiniMeToken token = MiniMeToken(address(uint160(_token)));
|
||||
uint balance = token.balanceOf(address(this));
|
||||
token.transfer(controller, balance);
|
||||
emit ClaimedTokens(_token, controller, balance);
|
||||
}
|
||||
|
||||
////////////////
|
||||
// Events
|
||||
////////////////
|
||||
event ClaimedTokens(address indexed _token, address indexed _controller, uint _amount);
|
||||
event Transfer(address indexed _from, address indexed _to, uint256 _amount);
|
||||
event NewCloneToken(address indexed _cloneToken, uint snapshotBlock);
|
||||
event Approval(
|
||||
address indexed _owner,
|
||||
address indexed _spender,
|
||||
uint256 _amount
|
||||
);
|
||||
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
pragma solidity ^0.5.7;
|
||||
|
||||
import "./TokenFactory.sol";
|
||||
import "./MiniMeToken.sol";
|
||||
|
||||
////////////////
|
||||
// MiniMeTokenFactory
|
||||
////////////////
|
||||
|
||||
/**
|
||||
* @dev This contract is used to generate clone contracts from a contract.
|
||||
* In solidity this is the way to create a contract from a contract of the
|
||||
* same class
|
||||
*/
|
||||
contract MiniMeTokenFactory is TokenFactory {
|
||||
|
||||
/**
|
||||
* @notice Update the DApp by creating a new token with new functionalities
|
||||
* the msg.sender becomes the controller of this clone token
|
||||
* @param _parentToken Address of the token being cloned
|
||||
* @param _snapshotBlock Block of the parent token that will
|
||||
* determine the initial distribution of the clone token
|
||||
* @param _tokenName Name of the new token
|
||||
* @param _decimalUnits Number of decimals of the new token
|
||||
* @param _tokenSymbol Token Symbol for the new token
|
||||
* @param _transfersEnabled If true, tokens will be able to be transferred
|
||||
* @return The address of the new token contract
|
||||
*/
|
||||
function createCloneToken(
|
||||
address _parentToken,
|
||||
uint _snapshotBlock,
|
||||
string memory _tokenName,
|
||||
uint8 _decimalUnits,
|
||||
string memory _tokenSymbol,
|
||||
bool _transfersEnabled
|
||||
) public returns (address payable) {
|
||||
MiniMeToken newToken = new MiniMeToken(
|
||||
address(this),
|
||||
_parentToken,
|
||||
_snapshotBlock,
|
||||
_tokenName,
|
||||
_decimalUnits,
|
||||
_tokenSymbol,
|
||||
_transfersEnabled
|
||||
);
|
||||
|
||||
newToken.changeController(msg.sender);
|
||||
return address(newToken);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,108 @@
|
|||
pragma solidity ^0.5.7;
|
||||
|
||||
import "./ERC20Token.sol";
|
||||
|
||||
|
||||
contract MiniMeTokenInterface is ERC20Token {
|
||||
|
||||
/**
|
||||
* @notice `msg.sender` approves `_spender` to send `_amount` tokens on
|
||||
* its behalf, and then a function is triggered in the contract that is
|
||||
* being approved, `_spender`. This allows users to use their tokens to
|
||||
* interact with contracts in one function call instead of two
|
||||
* @param _spender The address of the contract able to transfer the tokens
|
||||
* @param _amount The amount of tokens to be approved for transfer
|
||||
* @return True if the function call was successful
|
||||
*/
|
||||
function approveAndCall(
|
||||
address _spender,
|
||||
uint256 _amount,
|
||||
bytes memory _extraData
|
||||
)
|
||||
public
|
||||
returns (bool success);
|
||||
|
||||
/**
|
||||
* @notice Creates a new clone token with the initial distribution being
|
||||
* this token at `_snapshotBlock`
|
||||
* @param _cloneTokenName Name of the clone token
|
||||
* @param _cloneDecimalUnits Number of decimals of the smallest unit
|
||||
* @param _cloneTokenSymbol Symbol of the clone token
|
||||
* @param _snapshotBlock Block when the distribution of the parent token is
|
||||
* copied to set the initial distribution of the new clone token;
|
||||
* if the block is zero than the actual block, the current block is used
|
||||
* @param _transfersEnabled True if transfers are allowed in the clone
|
||||
* @return The address of the new MiniMeToken Contract
|
||||
*/
|
||||
function createCloneToken(
|
||||
string memory _cloneTokenName,
|
||||
uint8 _cloneDecimalUnits,
|
||||
string memory _cloneTokenSymbol,
|
||||
uint _snapshotBlock,
|
||||
bool _transfersEnabled
|
||||
)
|
||||
public
|
||||
returns(address);
|
||||
|
||||
/**
|
||||
* @notice Generates `_amount` tokens that are assigned to `_owner`
|
||||
* @param _owner The address that will be assigned the new tokens
|
||||
* @param _amount The quantity of tokens generated
|
||||
* @return True if the tokens are generated correctly
|
||||
*/
|
||||
function generateTokens(
|
||||
address _owner,
|
||||
uint _amount
|
||||
)
|
||||
public
|
||||
returns (bool);
|
||||
|
||||
/**
|
||||
* @notice Burns `_amount` tokens from `_owner`
|
||||
* @param _owner The address that will lose the tokens
|
||||
* @param _amount The quantity of tokens to burn
|
||||
* @return True if the tokens are burned correctly
|
||||
*/
|
||||
function destroyTokens(
|
||||
address _owner,
|
||||
uint _amount
|
||||
)
|
||||
public
|
||||
returns (bool);
|
||||
|
||||
/**
|
||||
* @notice Enables token holders to transfer their tokens freely if true
|
||||
* @param _transfersEnabled True if transfers are allowed in the clone
|
||||
*/
|
||||
function enableTransfers(bool _transfersEnabled) public;
|
||||
|
||||
/**
|
||||
* @notice This method can be used by the controller to extract mistakenly
|
||||
* sent tokens to this contract.
|
||||
* @param _token The address of the token contract that you want to recover
|
||||
* set to 0 in case you want to extract ether.
|
||||
*/
|
||||
function claimTokens(address _token) public;
|
||||
|
||||
/**
|
||||
* @dev Queries the balance of `_owner` at a specific `_blockNumber`
|
||||
* @param _owner The address from which the balance will be retrieved
|
||||
* @param _blockNumber The block number when the balance is queried
|
||||
* @return The balance at `_blockNumber`
|
||||
*/
|
||||
function balanceOfAt(
|
||||
address _owner,
|
||||
uint _blockNumber
|
||||
)
|
||||
public
|
||||
view
|
||||
returns (uint);
|
||||
|
||||
/**
|
||||
* @notice Total amount of tokens at a specific `_blockNumber`.
|
||||
* @param _blockNumber The block number when the totalSupply is queried
|
||||
* @return The total amount of tokens at `_blockNumber`
|
||||
*/
|
||||
function totalSupplyAt(uint _blockNumber) public view returns(uint);
|
||||
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
pragma solidity ^0.5.7;
|
||||
/**
|
||||
* @dev The token controller contract must implement these functions
|
||||
*/
|
||||
interface TokenController {
|
||||
/**
|
||||
* @notice Called when `_owner` sends ether to the MiniMe Token contract
|
||||
* @param _owner The address that sent the ether to create tokens
|
||||
* @return True if the ether is accepted, false if it throws
|
||||
*/
|
||||
function proxyPayment(address _owner) external payable returns(bool);
|
||||
|
||||
/**
|
||||
* @notice Notifies the controller about a token transfer allowing the
|
||||
* controller to react if desired
|
||||
* @param _from The origin of the transfer
|
||||
* @param _to The destination of the transfer
|
||||
* @param _amount The amount of the transfer
|
||||
* @return False if the controller does not authorize the transfer
|
||||
*/
|
||||
function onTransfer(address _from, address _to, uint _amount) external returns(bool);
|
||||
|
||||
/**
|
||||
* @notice Notifies the controller about an approval allowing the
|
||||
* controller to react if desired
|
||||
* @param _owner The address that calls `approve()`
|
||||
* @param _spender The spender in the `approve()` call
|
||||
* @param _amount The amount in the `approve()` call
|
||||
* @return False if the controller does not authorize the approval
|
||||
*/
|
||||
function onApprove(address _owner, address _spender, uint _amount) external
|
||||
returns(bool);
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
pragma solidity ^0.5.7;
|
||||
|
||||
contract TokenFactory {
|
||||
function createCloneToken(
|
||||
address _parentToken,
|
||||
uint _snapshotBlock,
|
||||
string memory _tokenName,
|
||||
uint8 _decimalUnits,
|
||||
string memory _tokenSymbol,
|
||||
bool _transfersEnabled
|
||||
) public returns (address payable);
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
{
|
||||
"contracts": [
|
||||
"contracts/**"
|
||||
],
|
||||
"buildDir": "dist/",
|
||||
"config": "embarkConfig/",
|
||||
"versions": {
|
||||
"web3": "1.0.0-beta",
|
||||
"solc": "0.5.10",
|
||||
"ipfs-api": "17.2.4"
|
||||
},
|
||||
"plugins": {
|
||||
"embark-solium": {},
|
||||
"embarkjs-connector-web3": {},
|
||||
"embark-solc": {}
|
||||
},
|
||||
"options": {
|
||||
"solc": {
|
||||
"optimize": true,
|
||||
"optimize-runs": 200
|
||||
}
|
||||
},
|
||||
"generationDir": "src/embarkArtifacts"
|
||||
}
|
|
@ -0,0 +1,131 @@
|
|||
module.exports = {
|
||||
// applies to all environments
|
||||
default: {
|
||||
enabled: true,
|
||||
rpcHost: "localhost", // HTTP-RPC server listening interface (default: "localhost")
|
||||
rpcPort: 8545, // HTTP-RPC server listening port (default: 8545)
|
||||
rpcCorsDomain: "auto", // Comma separated list of domains from which to accept cross origin requests (browser enforced)
|
||||
// When set to "auto", Embark will automatically set the cors to the address of the webserver
|
||||
wsRPC: true, // Enable the WS-RPC server
|
||||
wsOrigins: "auto", // Origins from which to accept websockets requests
|
||||
// When set to "auto", Embark will automatically set the cors to the address of the webserver
|
||||
wsHost: "localhost", // WS-RPC server listening interface (default: "localhost")
|
||||
wsPort: 8546, // WS-RPC server listening port (default: 8546)
|
||||
|
||||
// Accounts to use as node accounts
|
||||
// The order here corresponds to the order of `web3.eth.getAccounts`, so the first one is the `defaultAccount`
|
||||
accounts: [
|
||||
{
|
||||
nodeAccounts: true, // Accounts use for the node
|
||||
numAddresses: 2, // Number of addresses/accounts (defaults to 1)
|
||||
password: "embarkConfig/development/password" // Password file for the accounts
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
// default environment, merges with the settings in default
|
||||
// assumed to be the intended environment by `embark run` and `embark blockchain`
|
||||
development: {
|
||||
ethereumClientName: "geth", // Can be geth or parity (default:geth)
|
||||
//ethereumClientBin: "geth", // path to the client binary. Useful if it is not in the global PATH
|
||||
networkType: "custom", // Can be: testnet, rinkeby, livenet or custom, in which case, it will use the specified networkId
|
||||
networkId: 1337, // Network id used when networkType is custom
|
||||
isDev: true, // Uses and ephemeral proof-of-authority network with a pre-funded developer account, mining enabled
|
||||
datadir: ".embark/development/datadir", // Data directory for the databases and keystore (Geth 1.8.15 and Parity 2.0.4 can use the same base folder, till now they does not conflict with each other)
|
||||
mineWhenNeeded: true, // Uses our custom script (if isDev is false) to mine only when needed
|
||||
nodiscover: true, // Disables the peer discovery mechanism (manual peer addition)
|
||||
maxpeers: 0, // Maximum number of network peers (network disabled if set to 0) (default: 25)
|
||||
proxy: true, // Proxy is used to present meaningful information about transactions
|
||||
targetGasLimit: 8000000, // Target gas limit sets the artificial target gas floor for the blocks to mine
|
||||
simulatorBlocktime: 0 // Specify blockTime in seconds for automatic mining. Default is 0 and no auto-mining.
|
||||
},
|
||||
|
||||
// merges with the settings in default
|
||||
// used with "embark run privatenet" and/or "embark blockchain privatenet"
|
||||
privatenet: {
|
||||
networkType: "custom",
|
||||
networkId: 1337,
|
||||
isDev: false,
|
||||
datadir: ".embark/privatenet/datadir",
|
||||
// -- mineWhenNeeded --
|
||||
// This options is only valid when isDev is false.
|
||||
// Enabling this option uses our custom script to mine only when needed.
|
||||
// Embark creates a development account for you (using `geth account new`) and funds the account. This account can be used for
|
||||
// development (and even imported in to MetaMask). To enable correct usage, a password for this account must be specified
|
||||
// in the `account > password` setting below.
|
||||
// NOTE: once `mineWhenNeeded` is enabled, you must run an `embark reset` on your dApp before running
|
||||
// `embark blockchain` or `embark run` for the first time.
|
||||
mineWhenNeeded: true,
|
||||
// -- genesisBlock --
|
||||
// This option is only valid when mineWhenNeeded is true (which is only valid if isDev is false).
|
||||
// When enabled, geth uses POW to mine transactions as it would normally, instead of using POA as it does in --dev mode.
|
||||
// On the first `embark blockchain or embark run` after this option is enabled, geth will create a new chain with a
|
||||
// genesis block, which can be configured using the `genesisBlock` configuration option below.
|
||||
genesisBlock: "embarkConfig/privatenet/genesis.json", // Genesis block to initiate on first creation of a development node
|
||||
nodiscover: true,
|
||||
maxpeers: 0,
|
||||
proxy: true,
|
||||
accounts: [
|
||||
{
|
||||
nodeAccounts: true,
|
||||
password: "embarkConfig/privatenet/password" // Password to unlock the account
|
||||
}
|
||||
],
|
||||
targetGasLimit: 8000000,
|
||||
simulatorBlocktime: 0
|
||||
},
|
||||
|
||||
privateparitynet: {
|
||||
ethereumClientName: "parity",
|
||||
networkType: "custom",
|
||||
networkId: 1337,
|
||||
isDev: false,
|
||||
genesisBlock: "embarkConfig/privatenet/genesis-parity.json", // Genesis block to initiate on first creation of a development node
|
||||
datadir: ".embark/privatenet/datadir",
|
||||
mineWhenNeeded: false,
|
||||
nodiscover: true,
|
||||
maxpeers: 0,
|
||||
proxy: true,
|
||||
accounts: [
|
||||
{
|
||||
nodeAccounts: true,
|
||||
password: "embarkConfig/privatenet/password"
|
||||
}
|
||||
],
|
||||
targetGasLimit: 8000000,
|
||||
simulatorBlocktime: 0
|
||||
},
|
||||
|
||||
// merges with the settings in default
|
||||
// used with "embark run testnet" and/or "embark blockchain testnet"
|
||||
testnet: {
|
||||
networkType: "testnet",
|
||||
syncMode: "light",
|
||||
accounts: [
|
||||
{
|
||||
nodeAccounts: true,
|
||||
password: "embarkConfig/testnet/password"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
// merges with the settings in default
|
||||
// used with "embark run livenet" and/or "embark blockchain livenet"
|
||||
livenet: {
|
||||
networkType: "livenet",
|
||||
syncMode: "light",
|
||||
rpcCorsDomain: "http://localhost:8000",
|
||||
wsOrigins: "http://localhost:8000",
|
||||
accounts: [
|
||||
{
|
||||
nodeAccounts: true,
|
||||
password: "embarkConfig/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: {
|
||||
//}
|
||||
};
|
|
@ -0,0 +1,46 @@
|
|||
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
|
||||
},
|
||||
|
||||
// default environment, merges with the settings in default
|
||||
// assumed to be the intended environment by `embark run`
|
||||
development: {
|
||||
connection: {
|
||||
host: "localhost", // Host of the blockchain node
|
||||
port: 8546, // Port of the blockchain 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
|
||||
}
|
||||
*/
|
||||
//}
|
||||
};
|
|
@ -0,0 +1,291 @@
|
|||
const LICENSE_PRICE = "10000000000000000000"; // 10 * Math.pow(10, 18)
|
||||
const ARB_LICENSE_PRICE = "10000000000000000000"; // 10 * Math.pow(10, 18)
|
||||
const FEE_MILLI_PERCENT = "1000"; // 1 percent
|
||||
const BURN_ADDRESS = "0x0000000000000000000000000000000000000002";
|
||||
|
||||
const dataMigration = require('./data.js');
|
||||
|
||||
let secret = {};
|
||||
try {
|
||||
secret = require('../.secret.json');
|
||||
} catch(err) {
|
||||
console.dir("warning: .secret.json file not found; this is only needed to deploy to testnet or livenet etc..");
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
// default applies to all environments
|
||||
default: {
|
||||
// Blockchain node to deploy the contracts
|
||||
deployment: {
|
||||
host: "localhost", // Host of the blockchain node
|
||||
port: 8546, // Port of the blockchain node
|
||||
type: "ws" // Type of connection (ws or rpc),
|
||||
// Accounts to use instead of the default account to populate your wallet
|
||||
// The order here corresponds to the order of `web3.eth.getAccounts`, so the first one is the `defaultAccount`
|
||||
/*,accounts: [
|
||||
{
|
||||
privateKey: "your_private_key",
|
||||
balance: "5 ether" // You can set the balance of the account in the dev environment
|
||||
// Balances are in Wei, but you can specify the unit with its name
|
||||
},
|
||||
{
|
||||
privateKeyFile: "path/to/file", // Either a keystore or a list of keys, separated by , or ;
|
||||
password: "passwordForTheKeystore" // Needed to decrypt the keystore file
|
||||
},
|
||||
{
|
||||
mnemonic: "12 word mnemonic",
|
||||
addressIndex: "0", // Optionnal. The index to start getting the address
|
||||
numAddresses: "1", // Optionnal. The number of addresses to get
|
||||
hdpath: "m/44'/60'/0'/0/" // Optionnal. HD derivation path
|
||||
},
|
||||
{
|
||||
"nodeAccounts": true // Uses the Ethereum node's accounts
|
||||
}
|
||||
]*/
|
||||
},
|
||||
// order of connections the dapp should connect to
|
||||
dappConnection: [
|
||||
"$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 explicity specified inside the
|
||||
// contracts section.
|
||||
strategy: 'explicit',
|
||||
|
||||
contracts: {
|
||||
OwnedUpgradeabilityProxy: {
|
||||
deploy: false
|
||||
},
|
||||
License: {
|
||||
deploy: false
|
||||
},
|
||||
SellerLicense: {
|
||||
instanceOf: "License",
|
||||
args: [
|
||||
"$SNT",
|
||||
LICENSE_PRICE,
|
||||
BURN_ADDRESS // TODO: replace with "$StakingPool"
|
||||
]
|
||||
},
|
||||
SellerLicenseProxy: {
|
||||
instanceOf: "OwnedUpgradeabilityProxy"
|
||||
},
|
||||
ArbitrationLicense: {
|
||||
args: [
|
||||
"$SNT",
|
||||
ARB_LICENSE_PRICE,
|
||||
BURN_ADDRESS // TODO: replace with "$StakingPool"
|
||||
]
|
||||
},
|
||||
ArbitrationLicenseProxy: {
|
||||
instanceOf: "OwnedUpgradeabilityProxy"
|
||||
},
|
||||
"MetadataStore": {
|
||||
args: ["$SellerLicense", "$ArbitrationLicense"]
|
||||
},
|
||||
MetadataStoreProxy: {
|
||||
instanceOf: "OwnedUpgradeabilityProxy"
|
||||
},
|
||||
"RLPReader": {
|
||||
file: 'tabookey-gasless/contracts/RLPReader.sol'
|
||||
},
|
||||
"RelayHub": {
|
||||
file: 'tabookey-gasless/contracts/RelayHub.sol'
|
||||
},
|
||||
EscrowRelay: {
|
||||
args: ["$MetadataStoreProxy", "$EscrowProxy", "$SNT"],
|
||||
deps: ['RelayHub'],
|
||||
onDeploy: [
|
||||
"EscrowRelay.methods.setRelayHubAddress('$RelayHub').send()",
|
||||
"RelayHub.methods.depositFor('$EscrowRelay').send({value: 1000000000000000000})"
|
||||
]
|
||||
},
|
||||
Escrow: {
|
||||
args: ["0x0000000000000000000000000000000000000000", "$SellerLicense", "$ArbitrationLicense", "$MetadataStore", "$KyberFeeBurner", FEE_MILLI_PERCENT]
|
||||
},
|
||||
EscrowProxy: {
|
||||
instanceOf: "OwnedUpgradeabilityProxy"
|
||||
},
|
||||
"MiniMeToken": { "deploy": false },
|
||||
"MiniMeTokenFactory": { },
|
||||
"Fees": {
|
||||
"deploy": false
|
||||
},
|
||||
"SNT": {
|
||||
"instanceOf": "MiniMeToken",
|
||||
"args": [
|
||||
"$MiniMeTokenFactory",
|
||||
"0x0000000000000000000000000000000000000000",
|
||||
0,
|
||||
"TestMiniMeToken",
|
||||
18,
|
||||
"STT",
|
||||
true
|
||||
]
|
||||
},
|
||||
|
||||
/*
|
||||
"StakingPool": {
|
||||
file: 'staking-pool/contracts/StakingPool.sol',
|
||||
args: [
|
||||
"$SNT"
|
||||
]
|
||||
},
|
||||
*/
|
||||
|
||||
KyberNetworkProxy: {
|
||||
},
|
||||
KyberFeeBurner: { // TODO: replace BURN_ADDRESS with "$StakingPool"
|
||||
args: ["$SNT", BURN_ADDRESS, "$KyberNetworkProxy", "0x0000000000000000000000000000000000000000"]
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// default environment, merges with the settings in default
|
||||
// assumed to be the intended environment by `embark run`
|
||||
development: {
|
||||
contracts: {
|
||||
StandardToken: { },
|
||||
DAI: { instanceOf: "StandardToken", onDeploy: ["DAI.methods.mint('$accounts[0]', '20000000000000000000').send()"] },
|
||||
MKR: { instanceOf: "StandardToken", onDeploy: ["MKR.methods.mint('$accounts[0]', '20000000000000000000').send()"] }
|
||||
},
|
||||
deployment: {
|
||||
// The order here corresponds to the order of `web3.eth.getAccounts`, so the first one is the `defaultAccount`
|
||||
accounts: [
|
||||
{
|
||||
nodeAccounts: true
|
||||
},
|
||||
{
|
||||
mnemonic: "foster gesture flock merge beach plate dish view friend leave drink valley shield list enemy",
|
||||
balance: "5 ether",
|
||||
numAddresses: "10"
|
||||
}
|
||||
]
|
||||
},
|
||||
afterDeploy: dataMigration.bind(null, LICENSE_PRICE, ARB_LICENSE_PRICE, FEE_MILLI_PERCENT, BURN_ADDRESS)
|
||||
},
|
||||
|
||||
// merges with the settings in default
|
||||
// used with "embark run privatenet"
|
||||
privatenet: {
|
||||
},
|
||||
|
||||
// merges with the settings in default
|
||||
// used with "embark run testnet"
|
||||
testnet: {
|
||||
tracking: 'shared.rinkeby.json',
|
||||
deployment: {
|
||||
accounts: [
|
||||
{
|
||||
mnemonic: secret.mnemonic,
|
||||
hdpath: secret.hdpath || "m/44'/60'/0'/0/",
|
||||
numAddresses: "10"
|
||||
}
|
||||
],
|
||||
host: `rinkeby.infura.io/${secret.infuraKey}`,
|
||||
port: false,
|
||||
protocol: 'https',
|
||||
type: "rpc"
|
||||
},
|
||||
afterDeploy: dataMigration.bind(null, LICENSE_PRICE, ARB_LICENSE_PRICE, FEE_MILLI_PERCENT, BURN_ADDRESS),
|
||||
dappConnection: ["$WEB3"],
|
||||
contracts: {
|
||||
StandardToken: { },
|
||||
DAI: { instanceOf: "StandardToken", onDeploy: ["DAI.methods.mint('$accounts[0]', '20000000000000000000').send()"] },
|
||||
MKR: { instanceOf: "StandardToken", onDeploy: ["MKR.methods.mint('$accounts[0]', '20000000000000000000').send()"] },
|
||||
KyberNetworkProxy: {
|
||||
// https://developer.kyber.network/docs/Environments-Rinkeby/
|
||||
address: "0xF77eC7Ed5f5B9a5aee4cfa6FFCaC6A4C315BaC76"
|
||||
},
|
||||
RelayHub: {
|
||||
address: '0xd216153c06e857cd7f72665e0af1d7d82172f494'
|
||||
},
|
||||
EscrowRelay: {
|
||||
args: ["$MetadataStoreProxy", "$EscrowProxy", "$SNT"],
|
||||
deps: ['RelayHub'],
|
||||
onDeploy: [
|
||||
"EscrowRelay.methods.setRelayHubAddress('$RelayHub').send()",
|
||||
"RelayHub.methods.depositFor('$EscrowRelay').send({value: 10000000000000000})"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
ropsten: {
|
||||
gasPrice: "10000000000",
|
||||
tracking: 'shared.ropsten.json',
|
||||
contracts: {
|
||||
EscrowRelay: {
|
||||
args: ["$MetadataStoreProxy", "$EscrowProxy", "$SNT"],
|
||||
deps: ['RelayHub'],
|
||||
onDeploy: [
|
||||
"EscrowRelay.methods.setRelayHubAddress('$RelayHub').send({gasPrice: 20000000000, gas: 1000000})",
|
||||
"RelayHub.methods.depositFor('$EscrowRelay').send({gasPrice: 20000000000, value: 100000000000000000, gas: 1000000})"
|
||||
]
|
||||
},
|
||||
Escrow: {
|
||||
args: ["0x0000000000000000000000000000000000000000", "$SellerLicenseProxy", "$ArbitrationLicenseProxy", "$MetadataStoreProxy", BURN_ADDRESS, FEE_MILLI_PERCENT]
|
||||
},
|
||||
SNT: {
|
||||
address: "0xc55cf4b03948d7ebc8b9e8bad92643703811d162"
|
||||
},
|
||||
"RLPReader": {
|
||||
deploy: false
|
||||
},
|
||||
"RelayHub": {
|
||||
address: "0x1349584869A1C7b8dc8AE0e93D8c15F5BB3B4B87"
|
||||
},
|
||||
"MiniMeTokenFactory": {
|
||||
deploy: false
|
||||
},
|
||||
KyberNetworkProxy: {
|
||||
// https://developer.kyber.network/docs/Environments-Ropsten/
|
||||
address: "0x818E6FECD516Ecc3849DAf6845e3EC868087B755"
|
||||
}
|
||||
},
|
||||
deployment: {
|
||||
accounts: [
|
||||
{
|
||||
mnemonic: secret.mnemonic,
|
||||
hdpath: secret.hdpath || "m/44'/60'/0'/0/",
|
||||
numAddresses: "10"
|
||||
}
|
||||
],
|
||||
host: `ropsten.infura.io/${secret.infuraKey}`,
|
||||
port: false,
|
||||
protocol: 'https',
|
||||
type: "rpc"
|
||||
},
|
||||
afterDeploy: dataMigration.bind(null, LICENSE_PRICE, ARB_LICENSE_PRICE, FEE_MILLI_PERCENT, BURN_ADDRESS),
|
||||
dappConnection: ["$WEB3"]
|
||||
},
|
||||
|
||||
// merges with the settings in default
|
||||
// used with "embark run livenet"
|
||||
livenet: {
|
||||
contracts: {
|
||||
KyberNetworkProxy: {
|
||||
// https://developer.kyber.network/docs/Environments-Mainnet/
|
||||
address: "0x818E6FECD516Ecc3849DAf6845e3EC868087B755"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// you can name an environment with specific settings and then specify with
|
||||
// "embark run custom_name" or "embark blockchain custom_name"
|
||||
//custom_name: {
|
||||
//}
|
||||
};
|
|
@ -0,0 +1,274 @@
|
|||
module.exports = async (licensePrice, arbitrationLicensePrice, feeMilliPercent, burnAddress, deps) => {
|
||||
try {
|
||||
const addresses = await deps.web3.eth.getAccounts();
|
||||
const main = addresses[0];
|
||||
|
||||
{
|
||||
console.log('Setting the initial SellerLicense "template", and calling the init() function');
|
||||
|
||||
const abiEncode = deps.contracts.SellerLicense.methods.init(
|
||||
deps.contracts.SNT.options.address,
|
||||
licensePrice,
|
||||
burnAddress
|
||||
).encodeABI();
|
||||
|
||||
const receipt = await deps.contracts.SellerLicenseProxy.methods.upgradeToAndCall(deps.contracts.SellerLicense.options.address, abiEncode).send({from: main, gas: 1000000});
|
||||
|
||||
console.log(`Setting done and was a ${(receipt.status === true || receipt.status === 1) ? 'success' : 'failure'}`);
|
||||
}
|
||||
|
||||
|
||||
{
|
||||
console.log('Setting the initial ArbitrationLicense "template", and calling the init() function');
|
||||
|
||||
const abiEncode = deps.contracts.ArbitrationLicense.methods.init(
|
||||
deps.contracts.SNT.options.address,
|
||||
arbitrationLicensePrice,
|
||||
burnAddress
|
||||
).encodeABI();
|
||||
|
||||
const receipt = await deps.contracts.ArbitrationLicenseProxy.methods.upgradeToAndCall(deps.contracts.ArbitrationLicense.options.address, abiEncode).send({from: main, gas: 1000000});
|
||||
|
||||
console.log(`Setting done and was a ${(receipt.status === true || receipt.status === 1) ? 'success' : 'failure'}`);
|
||||
}
|
||||
|
||||
{
|
||||
console.log('Setting the initial MetadataStore "template", and calling the init() function');
|
||||
|
||||
const abiEncode = deps.contracts.MetadataStore.methods.init(
|
||||
deps.contracts.SellerLicenseProxy.options.address,
|
||||
deps.contracts.ArbitrationLicenseProxy.options.address
|
||||
).encodeABI();
|
||||
|
||||
const receipt = await deps.contracts.MetadataStoreProxy.methods.upgradeToAndCall(deps.contracts.MetadataStore.options.address, abiEncode).send({from: main, gas: 1000000});
|
||||
|
||||
console.log(`Setting done and was a ${(receipt.status === true || receipt.status === 1) ? 'success' : 'failure'}`);
|
||||
}
|
||||
|
||||
{
|
||||
console.log('Setting the initial Escrow "template", and calling the init() function');
|
||||
|
||||
const abiEncode = deps.contracts.Escrow.methods.init(
|
||||
deps.contracts.EscrowRelay.options.address,
|
||||
deps.contracts.SellerLicenseProxy.options.address,
|
||||
deps.contracts.ArbitrationLicenseProxy.options.address,
|
||||
deps.contracts.MetadataStoreProxy.options.address,
|
||||
burnAddress, // TODO: replace with StakingPool address
|
||||
feeMilliPercent
|
||||
).encodeABI();
|
||||
|
||||
// Here we are setting the initial "template", and calling the init() function
|
||||
const receipt = await deps.contracts.EscrowProxy.methods.upgradeToAndCall(deps.contracts.Escrow.options.address, abiEncode).send({from: main, gas: 1000000});
|
||||
|
||||
console.log(`Setting done and was a ${(receipt.status === true || receipt.status === 1) ? 'success' : 'failure'}`);
|
||||
}
|
||||
|
||||
deps.contracts.Escrow.options.address = deps.contracts.EscrowProxy.options.address;
|
||||
deps.contracts.SellerLicense.options.address = deps.contracts.SellerLicenseProxy.options.address;
|
||||
deps.contracts.ArbitrationLicense.options.address = deps.contracts.ArbitrationLicenseProxy.options.address;
|
||||
deps.contracts.MetadataStore.options.address = deps.contracts.MetadataStoreProxy.options.address;
|
||||
|
||||
const arbitrator = addresses[9];
|
||||
|
||||
const sntToken = 10000000;
|
||||
const balance = await deps.contracts.SNT.methods.balanceOf(main).call();
|
||||
if (balance !== '0') {
|
||||
return;
|
||||
}
|
||||
|
||||
console.log("Seeding data...");
|
||||
|
||||
console.log('Send ETH...');
|
||||
const value = 100 * Math.pow(10, 18);
|
||||
await Promise.all(addresses.slice(1, 10).map(async (address) => {
|
||||
return deps.web3.eth.sendTransaction({
|
||||
to: address,
|
||||
from: main,
|
||||
value: value.toString()
|
||||
});
|
||||
}));
|
||||
|
||||
console.log('Generate SNT...');
|
||||
await Promise.all(addresses.map(async (address) => {
|
||||
const generateToken = deps.contracts.SNT.methods.generateTokens(address, sntToken + '000000000000000000');
|
||||
const gas = await generateToken.estimateGas({from: main});
|
||||
return generateToken.send({from: main, gas});
|
||||
}));
|
||||
|
||||
console.log('Generate Standard Tokens');
|
||||
const weiToken = "5000000000000";
|
||||
await Promise.all(addresses.slice(0, 9).map(async (address) => {
|
||||
const generateToken = deps.contracts.StandardToken.methods.mint(address, weiToken.toString());
|
||||
const gas = await generateToken.estimateGas({from: main});
|
||||
return generateToken.send({from: main, gas});
|
||||
}));
|
||||
|
||||
console.log("Buy arbitration licenses");
|
||||
{
|
||||
const buyLicense = deps.contracts.ArbitrationLicense.methods.buy().encodeABI();
|
||||
let toSend = deps.contracts.SNT.methods.approveAndCall(deps.contracts.ArbitrationLicense._address, arbitrationLicensePrice, buyLicense);
|
||||
let gas = await toSend.estimateGas({from: arbitrator});
|
||||
await toSend.send({from: arbitrator, gas});
|
||||
|
||||
toSend = deps.contracts.SNT.methods.approveAndCall(deps.contracts.ArbitrationLicense._address, arbitrationLicensePrice, buyLicense);
|
||||
gas = await toSend.estimateGas({from: addresses[8]});
|
||||
await toSend.send({from: addresses[8], gas});
|
||||
|
||||
|
||||
// Accepting everyone
|
||||
toSend = deps.contracts.ArbitrationLicense.methods.changeAcceptAny(true);
|
||||
gas = await toSend.estimateGas({from: arbitrator});
|
||||
await toSend.send({from: arbitrator, gas});
|
||||
|
||||
}
|
||||
|
||||
console.log('Buy Licenses...');
|
||||
await Promise.all(addresses.slice(1, 7).map(async (address) => {
|
||||
const buyLicense = deps.contracts.SellerLicense.methods.buy().encodeABI();
|
||||
const toSend = deps.contracts.SNT.methods.approveAndCall(deps.contracts.SellerLicense._address, licensePrice, buyLicense);
|
||||
|
||||
const gas = await toSend.estimateGas({from: address});
|
||||
return toSend.send({from: address, gas});
|
||||
}));
|
||||
|
||||
console.log('Generating Offers...');
|
||||
const tokens = [deps.contracts.SNT._address, '0x0000000000000000000000000000000000000000'];
|
||||
const paymentMethods = [1, 2, 3];
|
||||
const usernames = ['Jonathan', 'Iuri', 'Anthony', 'Barry', 'Richard', 'Ricardo'];
|
||||
const locations = ['London', 'Montreal', 'Paris', 'Berlin'];
|
||||
const currencies = ['USD', 'EUR'];
|
||||
const offerStartIndex = 1;
|
||||
|
||||
const offerReceipts = await Promise.all(addresses.slice(offerStartIndex, offerStartIndex + 5).map(async (address) => {
|
||||
const addOffer = deps.contracts.MetadataStore.methods.addOffer(
|
||||
tokens[1],
|
||||
// TODO un hardcode token and add `approve` in the escrow creation below
|
||||
// tokens[Math.floor(Math.random() * tokens.length)],
|
||||
address,
|
||||
address,
|
||||
locations[Math.floor(Math.random() * locations.length)],
|
||||
currencies[Math.floor(Math.random() * currencies.length)],
|
||||
usernames[Math.floor(Math.random() * usernames.length)],
|
||||
[paymentMethods[Math.floor(Math.random() * paymentMethods.length)]],
|
||||
0,
|
||||
0,
|
||||
Math.floor(Math.random() * 100),
|
||||
arbitrator
|
||||
);
|
||||
|
||||
const gas = await addOffer.estimateGas({from: address});
|
||||
return addOffer.send({from: address, gas});
|
||||
}));
|
||||
|
||||
console.log('Creating escrows and rating them...');
|
||||
const val = 1000;
|
||||
const feeAmount = Math.round(val * (feeMilliPercent / (100 * 1000)));
|
||||
|
||||
const buyerAddress = addresses[offerStartIndex];
|
||||
const escrowStartIndex = offerStartIndex + 1;
|
||||
let receipt, hash, signature, nonce, created, escrowId;
|
||||
const PUBKEY_A = "0xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
|
||||
const PUBKEY_B = "0xBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB";
|
||||
|
||||
console.log('START', escrowStartIndex);
|
||||
console.log('RECiptys', offerReceipts.length);
|
||||
await Promise.all(addresses.slice(escrowStartIndex, escrowStartIndex + 1).map(async (creatorAddress, idx) => {
|
||||
console.log('Index = ', idx - offerStartIndex + escrowStartIndex);
|
||||
console.log('Address used:; ', creatorAddress);
|
||||
console.log('OWNER', offerReceipts[idx - offerStartIndex + escrowStartIndex].events.OfferAdded.returnValues.owner);
|
||||
const ethOfferId = offerReceipts[idx - offerStartIndex + escrowStartIndex].events.OfferAdded.returnValues.offerId;
|
||||
// TODO when we re-enable creating tokens too, use this to know
|
||||
// const token = offerReceipts[idx - offerStartIndex + escrowStartIndex].events.OfferAdded.returnValues.asset;
|
||||
|
||||
let gas;
|
||||
|
||||
// Create
|
||||
hash = await deps.contracts.MetadataStore.methods.getDataHash(usernames[offerStartIndex], PUBKEY_A, PUBKEY_B).call({from: buyerAddress});
|
||||
signature = await deps.web3.eth.sign(hash, buyerAddress);
|
||||
nonce = await deps.contracts.MetadataStore.methods.user_nonce(buyerAddress).call();
|
||||
|
||||
const creation = deps.contracts.Escrow.methods.createEscrow(ethOfferId, val, 140, PUBKEY_A, PUBKEY_B, locations[offerStartIndex], usernames[offerStartIndex], nonce, signature);
|
||||
gas = await creation.estimateGas({from: creatorAddress});
|
||||
receipt = await creation.send({from: creatorAddress, gas: gas + 1000});
|
||||
|
||||
created = receipt.events.Created;
|
||||
escrowId = created.returnValues.escrowId;
|
||||
|
||||
// Fund
|
||||
const fund = deps.contracts.Escrow.methods.fund(escrowId);
|
||||
gas = await fund.estimateGas({from: creatorAddress, value: val + feeAmount});
|
||||
receipt = await fund.send({from: creatorAddress, gas: gas + 1000, value: val + feeAmount});
|
||||
|
||||
// Release
|
||||
const release = deps.contracts.Escrow.methods.release(escrowId);
|
||||
gas = await release.estimateGas({from: creatorAddress});
|
||||
receipt = await release.send({from: creatorAddress, gas: gas + 1000});
|
||||
|
||||
// Rate
|
||||
const rating = Math.floor(Math.random() * 5) + 1;
|
||||
const rate = deps.contracts.Escrow.methods.rateTransaction(escrowId, rating);
|
||||
gas = await rate.estimateGas({from: buyerAddress});
|
||||
await rate.send({from: buyerAddress, gas: gas + 1000});
|
||||
}));
|
||||
|
||||
/*
|
||||
console.log('Creating arbitrations');
|
||||
|
||||
|
||||
await Promise.all(addresses.slice(escrowStartIndex, 5).map(async (creatorAddress, idx) => {
|
||||
const ethOfferId = offerReceipts[idx - offerStartIndex + escrowStartIndex].events.OfferAdded.returnValues.offerId;
|
||||
let gas, receipt;
|
||||
|
||||
const creation = deps.contracts.Escrow.methods.create_and_fund(buyerAddress, ethOfferId, val, expirationTime, FIAT, 13555);
|
||||
gas = await creation.estimateGas({from: creatorAddress, value: val + feeAmount});
|
||||
receipt = await creation.send({from: creatorAddress, value: val + feeAmount, gas: gas + 1000});
|
||||
const created = receipt.events.Created;
|
||||
const escrowId = created.returnValues.escrowId;
|
||||
|
||||
const pay = deps.contracts.Escrow.methods.pay(escrowId);
|
||||
gas = await pay.estimateGas({from: buyerAddress});
|
||||
receipt = await pay.send({from: buyerAddress, gas: gas + 1000});
|
||||
|
||||
const openCase = deps.contracts.Escrow.methods.openCase(escrowId, 'My Motive is...');
|
||||
gas = await openCase.estimateGas({from: buyerAddress});
|
||||
receipt = await openCase.send({from: buyerAddress, gas: gas + 1000});
|
||||
}));
|
||||
*/
|
||||
const accounts = await Promise.all(addresses.map(async(address) => {
|
||||
const ethBalance = await deps.web3.eth.getBalance(address);
|
||||
const sntBalance = await deps.contracts.SNT.methods.balanceOf(address).call();
|
||||
const isLicenseOwner = await deps.contracts.SellerLicense.methods.isLicenseOwner(address).call();
|
||||
let user = {};
|
||||
let offers = [];
|
||||
const isUser = await deps.contracts.MetadataStore.methods.users(address).call();
|
||||
if (isUser) {
|
||||
user = await deps.contracts.MetadataStore.methods.users(address).call();
|
||||
const offerIds = await deps.contracts.MetadataStore.methods.getOfferIds(address).call();
|
||||
offers = await Promise.all(offerIds.map(async(offerId) => (
|
||||
deps.contracts.MetadataStore.methods.offer(offerId).call()
|
||||
)));
|
||||
}
|
||||
return {
|
||||
address,
|
||||
isLicenseOwner,
|
||||
isUser,
|
||||
user,
|
||||
offers,
|
||||
ethBalance: deps.web3.utils.fromWei(ethBalance),
|
||||
sntBalance: deps.web3.utils.fromWei(sntBalance)
|
||||
};
|
||||
}));
|
||||
|
||||
console.log('Summary:');
|
||||
console.log('######################');
|
||||
accounts.forEach((account) => {
|
||||
console.log(`Address: ${account.address}:`);
|
||||
console.log(`License Owner: ${account.isLicenseOwner} ETH: ${account.ethBalance} SNT: ${account.sntBalance}`);
|
||||
console.log(`Is User: ${account.isUser} Username: ${account.user.username || 'N/A'} Offers: ${account.offers.length}`);
|
||||
console.log('');
|
||||
});
|
||||
} catch (e) {
|
||||
console.log("------- data seeding error ------- ");
|
||||
console.dir(e);
|
||||
}
|
||||
};
|
|
@ -0,0 +1 @@
|
|||
dev_password
|
|
@ -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: {
|
||||
//}
|
||||
};
|
|
@ -0,0 +1,3 @@
|
|||
module.exports = {
|
||||
enabled: true
|
||||
};
|
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,18 @@
|
|||
{
|
||||
"config": {
|
||||
"homesteadBlock": 0,
|
||||
"byzantiumBlock": 0,
|
||||
"daoForkSupport": true
|
||||
},
|
||||
"nonce": "0x0000000000000042",
|
||||
"difficulty": "0x0",
|
||||
"alloc": {
|
||||
"0x3333333333333333333333333333333333333333": {"balance": "15000000000000000000"}
|
||||
},
|
||||
"mixhash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"coinbase": "0x3333333333333333333333333333333333333333",
|
||||
"timestamp": "0x00",
|
||||
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"extraData": "0x",
|
||||
"gasLimit": "0x7a1200"
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
dev_password
|
|
@ -0,0 +1,60 @@
|
|||
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: {
|
||||
enabled: true,
|
||||
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: {
|
||||
enabled: false
|
||||
},
|
||||
|
||||
// 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: {
|
||||
//}
|
||||
};
|
|
@ -0,0 +1 @@
|
|||
test_password
|
|
@ -0,0 +1,3 @@
|
|||
module.exports = {
|
||||
enabled: false
|
||||
};
|
|
@ -0,0 +1,42 @@
|
|||
{
|
||||
"name": "status-teller-network",
|
||||
"version": "0.0.1",
|
||||
"description": "",
|
||||
"scripts": {
|
||||
"start": "react-scripts start",
|
||||
"build": "react-scripts build",
|
||||
"test": "react-scripts test",
|
||||
"eject": "react-scripts eject"
|
||||
},
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"homepage": "",
|
||||
"devDependencies": {},
|
||||
"dependencies": {
|
||||
"babel-loader": "^8.0.5",
|
||||
"bn.js": "^4.11.8",
|
||||
"clone-deep": "^4.0.1",
|
||||
"elliptic": "^6.5.0",
|
||||
"embark": "^4.0.2",
|
||||
"embark-solc": "^4.0.3",
|
||||
"embark-solium": "0.1.0",
|
||||
"embarkjs-connector-web3": "^4.0.0",
|
||||
"eth-ens-namehash": "^2.0.8",
|
||||
"ethereumjs-util": "^6.0.0",
|
||||
"staking-pool": "^0.0.1",
|
||||
"tabookey-gasless": "^0.4.0-beta2",
|
||||
"web3": "1.0.0-beta.37",
|
||||
"react": "^16.8.2",
|
||||
"react-dom": "^16.8.2",
|
||||
"react-scripts": "2.1.5"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"extends": "react-app"
|
||||
},
|
||||
"browserslist": [
|
||||
">0.2%",
|
||||
"not dead",
|
||||
"not ie <= 11",
|
||||
"not op_mini all"
|
||||
]
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta
|
||||
name="viewport"
|
||||
content="width=device-width, initial-scale=1, shrink-to-fit=no"
|
||||
/>
|
||||
<title>Status Teller Network</title>
|
||||
</head>
|
||||
<body>
|
||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||
<div id="root"></div>
|
||||
<!--
|
||||
This HTML file is a template.
|
||||
If you open it directly in the browser, you will see an empty page.
|
||||
|
||||
You can add webfonts, meta tags, or analytics to this file.
|
||||
The build step will place the bundled scripts into the <body> tag.
|
||||
|
||||
To begin the development, run `npm start` or `yarn start`.
|
||||
To create a production bundle, use `npm run build` or `yarn build`.
|
||||
-->
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,226 @@
|
|||
{
|
||||
"0x6341fd3daf94b748c72ced5a5b26028f2474f5f00d824504e4fa37a75767e177": {
|
||||
"contracts": {
|
||||
"0x3043b04ad856d169c8f0b0509c0bc63192dc7edd92d6933c58708298a0e381be": {
|
||||
"name": "ENSRegistry",
|
||||
"address": "0xe7410170f87102DF0055eB195163A03B7F2Bff4A"
|
||||
},
|
||||
"0x1b577600819d19882762a47860e0d23dad953ca145edee5e9d2e3d59a50ef0a5": {
|
||||
"name": "KyberNetworkProxy",
|
||||
"address": "0xF77eC7Ed5f5B9a5aee4cfa6FFCaC6A4C315BaC76"
|
||||
},
|
||||
"0xab973d34eccbfa264b59d1aff6bc7f8bc0f75d2a89d7e1bbad63d8a2cf73e765": {
|
||||
"name": "RelayHub",
|
||||
"address": "0xd216153c06e857cd7f72665e0af1d7d82172f494"
|
||||
},
|
||||
"0xc7736f10a733af68ff770bcb372acd39c464e9ac528c9bfdc053cdb33d289246": {
|
||||
"name": "RLPReader",
|
||||
"address": "0xae3940E52b649a1314f7d3025Ef94d6f337cCE92"
|
||||
},
|
||||
"0x6d4b2f1548974e845feb3ccce8937c463340f5d84922ab1720814fe9bb2862c3": {
|
||||
"name": "StandardToken",
|
||||
"address": "0x851F554A9Aa205182ca143F2A4c547785C5b9C37"
|
||||
},
|
||||
"0xe63becc7e5231b3f667f849428112631ddfddf638a1428bce5367ca054ad8286": {
|
||||
"name": "DAI",
|
||||
"address": "0x49BF147F3403879B829322cDE32919CA01960658"
|
||||
},
|
||||
"0x0a0933bde1f838ba180c1501539168600f549319d5ce32cbb9c7d0dc73ff0758": {
|
||||
"name": "SellerLicenseProxy",
|
||||
"address": "0xf714F5f462E0e68ad901F8C13e298569F6Fd661f"
|
||||
},
|
||||
"0x2593ad087110ec0f4c738870abc12933973759dd2b0aab743574cb2a92367107": {
|
||||
"name": "ArbitrationLicenseProxy",
|
||||
"address": "0xAbBC6D3990DB702ed00A51889248086327aF1A5A"
|
||||
},
|
||||
"0x66bc64129f6447bfd0b73a3909a7c3e7c598cf01e1d0c79b08f19d559fe34fd4": {
|
||||
"name": "MiniMeTokenFactory",
|
||||
"address": "0xbDe5EC593CBD896f4f7f01A8578792d96fae0486"
|
||||
},
|
||||
"0xc6fafd3c65e7e1c7b0addf0e6ba081862d057db14cea992db689d98e85dcdc9f": {
|
||||
"name": "MKR",
|
||||
"address": "0xf454112E88B6d7Fa2fE7538F4b84CfC5fD86CE54"
|
||||
},
|
||||
"0x2c3b03b9c312c790c5bd7fc46d6fc7ac647e42bf8ace12b8d64394abdad8fb21": {
|
||||
"name": "MetadataStoreProxy",
|
||||
"address": "0xF5b4F9d637c6b55152C5aaf1D5B58ae21c54a875"
|
||||
},
|
||||
"0x5c2c412b3c9f50bd52d751cd8b2622f0765e2a2a97d8f3bbb3e90e56b83a519e": {
|
||||
"name": "EscrowProxy",
|
||||
"address": "0x7d6394104f5175CB14bfAC56e807348F20Dc29f9"
|
||||
},
|
||||
"0x74573ee56f292d0d5e59f699e0225f72c68858d0677f62d52f4bea8e8651accc": {
|
||||
"name": "SNT",
|
||||
"address": "0x3C36db79598e7902b5D726af7C7d406d5Da8aF14"
|
||||
},
|
||||
"0x29e50f90a18564fa4e93664f08c1a5ea3508443297423be37673a9cd6dd875ed": {
|
||||
"name": "SellerLicense",
|
||||
"address": "0x1bcA5BCbf2aA8aafF2429e15a9A9C8B6a0681325"
|
||||
},
|
||||
"0xedf3e9504fbcdfca9661216a25c48edb4a02640bd9244bef4f74fb7c82dd64e5": {
|
||||
"name": "EscrowRelay",
|
||||
"address": "0x22CDf9a803d791638782fC152E0E4F472EA660f9"
|
||||
},
|
||||
"0x4e5c08aa149c247d984571083361f972365ec846ad1e1d5c6bcf58474d5fa536": {
|
||||
"name": "KyberFeeBurner",
|
||||
"address": "0x1e9420E2Ab23cC5f1fb4f7e2077A586A09Bb69aF"
|
||||
},
|
||||
"0xf396e0f6562aa338281d33b54e17230616bcaf844b80b15d5834786868372045": {
|
||||
"name": "ArbitrationLicense",
|
||||
"address": "0x53202def8E6e04a7Fb743532e5e1646f32BB120d"
|
||||
},
|
||||
"0x5518671e17ebae7843ec9b113429b8b8379d7cffc827d2259c9c0174dcf8e238": {
|
||||
"name": "MetadataStore",
|
||||
"address": "0x3b5b0820328Aa60B17c2192e94a8bba4F5020CCA"
|
||||
},
|
||||
"0x01d5f876801e3d01e55439fa123643fc75c09bb1d5779ed5869841c99b8b3a7a": {
|
||||
"name": "Escrow",
|
||||
"address": "0x5b8608DF47d4d5c16ED1ac9acc4EFC57C6E3453b"
|
||||
},
|
||||
"0xa8c1e6b830fe9e3d5c3314352b7db90e124aa361eb8e838555ec37a91f8562b2": {
|
||||
"name": "KyberNetworkProxy",
|
||||
"address": "0xF77eC7Ed5f5B9a5aee4cfa6FFCaC6A4C315BaC76"
|
||||
},
|
||||
"0xbb4a95581b19198f074b391c4cfacd9b64ce50c44bd14d3baa65c8c730517bc1": {
|
||||
"name": "RelayHub",
|
||||
"address": "0xd216153c06e857cd7f72665e0af1d7d82172f494"
|
||||
},
|
||||
"0x98398cd906dca8ecdc57c37049ae0ef1ee803489aab046e7a3bade70c755c403": {
|
||||
"name": "DAI",
|
||||
"address": "0x27FFe7e38D36e32EcE6E1FF12f93D3980b06CA3c"
|
||||
},
|
||||
"0xec59f5b11a29acafcfb616cb84d3985a918f6d816476c93c04d913150b0a499c": {
|
||||
"name": "MKR",
|
||||
"address": "0xec61AE9f48d7714802f7252F66e46a72Fc79Fc4C"
|
||||
},
|
||||
"0xad3c5e94cf912009bf4940e44a7e06b2942bed2ad736f253638b03de9bcea914": {
|
||||
"name": "SellerLicenseProxy",
|
||||
"address": "0x65A765505793d4B7D0872Ac266b5a8B745C2Ac0c"
|
||||
},
|
||||
"0x8a78d902010f6152d9d2394bf2318c3a848fdeada26be6b5ef781b6a84d6ea46": {
|
||||
"name": "ArbitrationLicenseProxy",
|
||||
"address": "0xF3F8501044A6519981b0763659bCAD0763d9a911"
|
||||
},
|
||||
"0x2ce4f7945512a0e430f4d73c95e955a937f9ca2abb9fe6c062b60d287c07d93b": {
|
||||
"name": "EscrowProxy",
|
||||
"address": "0x8B8034110DDB679746CFd2eA2B3aa35bBc6A8A5B"
|
||||
},
|
||||
"0x58c7aa33d79ff2ac4be5b4a4a031dba002bbb4d1cfad405cd0067c5cb1260306": {
|
||||
"name": "RLPReader",
|
||||
"address": "0x99f3191669Ce05d42E1912733985D2810a903DaA"
|
||||
},
|
||||
"0xadd39b686828e5e86cd78830e086b6f46b855f0d8267fddc054f46dc6ebf362f": {
|
||||
"name": "StandardToken",
|
||||
"address": "0x5E130075834810ce0e984132Ca7165382CB87e94"
|
||||
},
|
||||
"0x26114ac0d7c8eb9376fca18080a5b8aa87da83a49dbfa789a2299329b6d92836": {
|
||||
"name": "MetadataStoreProxy",
|
||||
"address": "0xB28126346603506Ea40a032765A9b851fE89c1eF"
|
||||
},
|
||||
"0x4a5a82fac72b1cc19c644e794a0229286d721daf2e345b95ebf43ec7d8de9fbe": {
|
||||
"name": "MiniMeTokenFactory",
|
||||
"address": "0xd232aba419100552276fF63A7863F3Fba6412f5d"
|
||||
},
|
||||
"0x6a1df6e1075bdf381d1bda8c3c8fb9b6ab603cebe981876048ef312b5ca5420d": {
|
||||
"name": "SNT",
|
||||
"address": "0xFEdeC04E4cde2c4cd27a199ba21f6736171A3C39"
|
||||
},
|
||||
"0x04081290301fe85cdb2b7c17f9af493bea3ea9e18348b415aa23153ca96e6ba7": {
|
||||
"name": "KyberFeeBurner",
|
||||
"address": "0x16292D56F1F9a4de4f09b2D154577984D7e87417"
|
||||
},
|
||||
"0x05f432711eaf049294f51b5af77f22ef5812202604dc9b307bba44405934b772": {
|
||||
"name": "ArbitrationLicense",
|
||||
"address": "0xE1Fa7c2857074cEdc20bf4070F24B48834360C75"
|
||||
},
|
||||
"0xa37eeba57e75f8e44fb930146800660c3dd2568891f662f6e6b013367572e80e": {
|
||||
"name": "EscrowRelay",
|
||||
"address": "0x9cB4d8dD0eE94Aa41E229b465952129dF93FBC2f"
|
||||
},
|
||||
"0x4919ec5e817e014dcfb4714e993a4eabd4a769afca5ee030d707e57540afff2c": {
|
||||
"name": "SellerLicense",
|
||||
"address": "0xf3963Ef61e3b380C6aF1FD197A14bDAc34860645"
|
||||
},
|
||||
"0x963bf16d22d118df1d09bca15c9902faee61dcb3011e0c9eacfe445f9de22e4b": {
|
||||
"name": "MetadataStore",
|
||||
"address": "0xf72213839a52028b4fc84f8cA90a0D049c1eD314"
|
||||
},
|
||||
"0xdb63d70594ddec6de1a9da2d6ca73a16c66ac4e46beb6d2532b1679052b90015": {
|
||||
"name": "Escrow",
|
||||
"address": "0xF63290032cfb043B704a22CC1aFaf90A810db2A5"
|
||||
},
|
||||
"0x4baef1235421717d9c68feba5ece50f02d404b33a5635fdba26c595395741f50": {
|
||||
"name": "KyberNetworkProxy",
|
||||
"address": "0xF77eC7Ed5f5B9a5aee4cfa6FFCaC6A4C315BaC76"
|
||||
},
|
||||
"0x669233c46987267322461347ed5f13edcd498da58f719976dc14d71223307173": {
|
||||
"name": "RelayHub",
|
||||
"address": "0xd216153c06e857cd7f72665e0af1d7d82172f494"
|
||||
},
|
||||
"0x0805905c13ba69e6d0109c73318eb64b5cfe02a9ba8a263d01fa70a18003d329": {
|
||||
"name": "RLPReader",
|
||||
"address": "0x455193E1A0434C6dBE00E90Ba198e36acEd86A4b"
|
||||
},
|
||||
"0xa42358beddec6294a6d64026e2456ff06910f73e12b4766043c97a52f60f1dd9": {
|
||||
"name": "EscrowProxy",
|
||||
"address": "0x98395d91FC1B8066C3B97b7c28663CA81Fb1531b"
|
||||
},
|
||||
"0x656bcc57b1941554e2398dac4529a4a239bf1c46e1f748394e95beadee770748": {
|
||||
"name": "DAI",
|
||||
"address": "0x087BAA82f7204Ed930ddD40c3f1b2c9aA25B68a1"
|
||||
},
|
||||
"0x7230d856dc70e1ec6e3b006a3456dd2d3eb06b2642e299c394c579f8244b560d": {
|
||||
"name": "SellerLicenseProxy",
|
||||
"address": "0x028De9aaE8Ab19C7ab643fBe8d5487DB0E492943"
|
||||
},
|
||||
"0x6ff69b61337b8ce3f6f7d26db2fb4dd8ed7571afa418a26cc392a74e513a3393": {
|
||||
"name": "ArbitrationLicenseProxy",
|
||||
"address": "0x3637Bac17065b06058D24527f578Ad8D04deEa4e"
|
||||
},
|
||||
"0x39849cbac15f0403188407c078f7eff4b5b96d87a95ca1f46060ae7af16dba9a": {
|
||||
"name": "MetadataStoreProxy",
|
||||
"address": "0x2a19a72D09b67967f066927A7fC52042e357dedc"
|
||||
},
|
||||
"0xfe5c3d5f91ffe42e177bc63eac53820d4ae48e7966d60db540cd51ae514cd55a": {
|
||||
"name": "MKR",
|
||||
"address": "0x701815622f4b46C50d19D1540525FEc243477CA2"
|
||||
},
|
||||
"0xd696f0155d503f9bbe17d0eca3abe4c52df7d2e19ca4db5887ac9cb769025040": {
|
||||
"name": "MiniMeTokenFactory",
|
||||
"address": "0x1f37e6243EeaEB2241C2e6a6D8944B81075Eb48D"
|
||||
},
|
||||
"0xc3c3cda956a344ef1c3e2d589bc5a6743c7c656ba5e1640e57568c620bdbb6ac": {
|
||||
"name": "StandardToken",
|
||||
"address": "0xd0e91f9CA8A4514001F7735158258eFf5C22d93D"
|
||||
},
|
||||
"0xf8b89ba816f68f551ef153790cd06aeba42314490d4c75545fa589d0d470b90d": {
|
||||
"name": "SNT",
|
||||
"address": "0x1e828cdABdf7F9a60654F8090900Fee89c8A2965"
|
||||
},
|
||||
"0xc74ae73d1af6af27ff89d7dffa1a4af26a5c3727514d33fb2d31adfc70e909b7": {
|
||||
"name": "KyberFeeBurner",
|
||||
"address": "0xE215B1eb240666BA7E7b845c849A55a110056345"
|
||||
},
|
||||
"0x9eda55b2b844000b7eb0ea55a110704b0209d25ecfe468367eab055479c055db": {
|
||||
"name": "SellerLicense",
|
||||
"address": "0x10A18eBfB1792C229DE82c08e33212896A50c201"
|
||||
},
|
||||
"0xeef7ea6c52b68a48279ee6706ca43b85afd3a117a3535002831d9aa805bb47dd": {
|
||||
"name": "EscrowRelay",
|
||||
"address": "0x3b039b735572ECa9B620541c65657F67D007F392"
|
||||
},
|
||||
"0xdf0516e57e1ff6ffcf014460c65e7c97004153dacb13763a67b2c5c44a07b887": {
|
||||
"name": "ArbitrationLicense",
|
||||
"address": "0x2F5D839162A3b59D9a6FCf2df0f5b9f035AE36ff"
|
||||
},
|
||||
"0x6a7ffacd295a88fd56ec93448f3b68061ea4f03fcba323130f051882791a69be": {
|
||||
"name": "MetadataStore",
|
||||
"address": "0x41578fB96e021A84a79447cbA9bc4D2A4523bbb8"
|
||||
},
|
||||
"0x84d2578600c1c6bc229531ff93e87c52f9379091fffed2fb4e2bd2dfcb63ab99": {
|
||||
"name": "Escrow",
|
||||
"address": "0xBb1C1407989936B787F7413F4360C71bE715BDd0"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,62 @@
|
|||
{
|
||||
"0x41941023680923e0fe4d74a34bdac8141f2540e3ae90623718e47d66d1ca4a2d": {
|
||||
"contracts": {
|
||||
"0x3043b04ad856d169c8f0b0509c0bc63192dc7edd92d6933c58708298a0e381be": {
|
||||
"name": "ENSRegistry",
|
||||
"address": "0x112234455c3a32fd11230c42e7bccd4a84e02010"
|
||||
},
|
||||
"0x4af953c2758aa8edcbcbeef13926c1cf17e7c29aea055ec99022260ca7c3d0c7": {
|
||||
"name": "KyberNetworkProxy",
|
||||
"address": "0x818E6FECD516Ecc3849DAf6845e3EC868087B755"
|
||||
},
|
||||
"0x72ef506bf1c7f442f2d4c5bfc97ba6061d9fb1bbbc345adbe7eb18ee583b9329": {
|
||||
"name": "SNT",
|
||||
"address": "0xc55cf4b03948d7ebc8b9e8bad92643703811d162"
|
||||
},
|
||||
"0x4f96b534860cd72e9388b7373c14f4d9bdc201a81bdccdc1b2922865de029497": {
|
||||
"name": "RelayHub",
|
||||
"address": "0x1349584869A1C7b8dc8AE0e93D8c15F5BB3B4B87"
|
||||
},
|
||||
"0x68dfb5ad82a1f92f56b313d36ed2f79376c88b2f37d58d2846e66491282eaf13": {
|
||||
"name": "KyberFeeBurner",
|
||||
"address": "0xb009e41d5c7daC38e02fCDb6ebb97f53ed6A950c"
|
||||
},
|
||||
"0x084dcee6b38fbfeacf8371e068678793efc36cf8a9a1d83427e70ef8613852d3": {
|
||||
"name": "EscrowProxy",
|
||||
"address": "0x06Ea1813Aa7CFf6D3eF109Be941C469F0c3e6cc6"
|
||||
},
|
||||
"0x33ce98f7b733f04dce6121e52d066acaedb6ca1e83be5ccc73d20391c02537f5": {
|
||||
"name": "MetadataStoreProxy",
|
||||
"address": "0xB7d1bC73F6220E214CFE2bE795462637f67d2023"
|
||||
},
|
||||
"0x9d4080fcd5bf7499bba97bd8c330b11cc064c604c2aa6cc79aef7ec7783f354b": {
|
||||
"name": "SellerLicenseProxy",
|
||||
"address": "0xB84b30c98BA8ff2A7f5DB3a14b438863FaBdDDDf"
|
||||
},
|
||||
"0xf7748af69578cd769431eec4a0daa1e13b6a51968170bcf37fd93a60edd5b249": {
|
||||
"name": "SellerLicense",
|
||||
"address": "0xbDEBeE4F6F4c64540309a8fE827891ea70177539"
|
||||
},
|
||||
"0xdc769c37e39d0464d5178e6c94a8d95bde8de0328f9ff694ab059c1ccf004fc6": {
|
||||
"name": "ArbitrationLicense",
|
||||
"address": "0x1315b1bC9532c08c44B5C4Ddc5F9c6A1831D272f"
|
||||
},
|
||||
"0xb61ac3f971c25164c5cf00fe4616c8c9ad0759848d62c9fb803bdb4914af19eb": {
|
||||
"name": "ArbitrationLicenseProxy",
|
||||
"address": "0x79647AD1d4bAFBbDb326A096A113aFc92158b03d"
|
||||
},
|
||||
"0x90be19004e959c27194e39559cbc7461b427237b7feec7de5166fae42877463a": {
|
||||
"name": "EscrowRelay",
|
||||
"address": "0x04d54b422DC59b8A539F4C2745b62649bb7541Ee"
|
||||
},
|
||||
"0xc605b00d6ddfbced735137ff16dc632d76770a1d1a2f3c68420de840c4b31e28": {
|
||||
"name": "MetadataStore",
|
||||
"address": "0x8bA9762695CAf7828DaFB279781f4574a5e7CAa0"
|
||||
},
|
||||
"0xa5d7276422517e35c151dad3d6eb01fbbde1e0594927ef876d7df8a9b5518bf2": {
|
||||
"name": "Escrow",
|
||||
"address": "0x11b32dA9418d82292dB926d32E558d62337Fd855"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
|
||||
ReactDOM.render(<h1>Test</h1>, document.getElementById('root'));
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue