Refactor some implementation

This commit is contained in:
Lyubomir Kiprov 2019-04-30 18:15:27 +03:00
parent b6e01e690b
commit a26091f59e
15 changed files with 340 additions and 275 deletions

View File

@ -20,30 +20,30 @@ module.exports = {
// 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: '1', // Number of addresses/accounts (defaults to 1)
password: 'config/development/password', // Password file for the accounts
},
// Below are additional accounts that will count as `nodeAccounts` in the `deployment` section of your contract config
// Those will not be unlocked in the node itself
{
privateKey:
'0xEFA9DB87A755C9D2B96F77BBCB9EF06CBDDFC01DB1A5129CE2649F73E9C2739C',
balance: '100 ether',
},
// {
// 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', // Optional. The index to start getting the address
// numAddresses: '1', // Optional. The number of addresses to get
// hdpath: "m/44'/60'/0'/0/", // Optional. HD derivation path
// },
],
// accounts: [
// {
// nodeAccounts: true, // Accounts use for the node
// numAddresses: '1', // Number of addresses/accounts (defaults to 1)
// password: 'config/development/password', // Password file for the accounts
// },
// Below are additional accounts that will count as `nodeAccounts` in the `deployment` section of your contract config
// Those will not be unlocked in the node itself
// {
// privateKey:
// '0xEFA9DB87A755C9D2B96F77BBCB9EF06CBDDFC01DB1A5129CE2649F73E9C2739C',
// balance: '100 ether',
// },
// {
// 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', // Optional. The index to start getting the address
// numAddresses: '1', // Optional. The number of addresses to get
// hdpath: "m/44'/60'/0'/0/", // Optional. HD derivation path
// },
// ],
},
// default environment, merges with the settings in default

View File

@ -4,8 +4,8 @@ module.exports = {
// 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),
port: 8545, // Port of the blockchain node
type: 'rpc', // 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: [
@ -28,6 +28,14 @@ module.exports = {
"nodeAccounts": true // Uses the Ethereum node's accounts
}
] */
accounts: [
{
privateKey:
'0xEFA9DB87A755C9D2B96F77BBCB9EF06CBDDFC01DB1A5129CE2649F73E9C2739C',
balance: '100 ether',
},
],
},
// order of connections the dapp should connect to
dappConnection: [
@ -51,12 +59,35 @@ module.exports = {
// contracts section.
// strategy: 'implicit',
// contracts: {
// Discover: {
// args: { _SNT: '0x744d70fdbe2ba4cf95131626614a1763df805b9e' },
// },
// MiniMeToken: { deploy: false },
// TestBancorFormula: { deploy: false },
// },
contracts: {
Discover: {
args: { _SNT: '0x744d70fdbe2ba4cf95131626614a1763df805b9e' },
},
MiniMeToken: { deploy: false },
TestBancorFormula: { deploy: false },
MiniMeTokenFactory: {},
SNT: {
from: '0x68C864373C6631984B646453138557A81224ACf6',
instanceOf: 'MiniMeToken',
args: [
'$MiniMeTokenFactory',
'0x0000000000000000000000000000000000000000',
0,
'TestMiniMeToken',
18,
'SNT',
true,
],
},
Discover: {
from: '0x68C864373C6631984B646453138557A81224ACf6',
args: ['$SNT'],
},
},
},

View File

@ -42,7 +42,7 @@ contract Discover is ApproveAndCallFallBack, BancorFormula {
Data[] public dapps;
mapping(bytes32 => uint) public id2index;
mapping(bytes32 => bool) existingIDs;
mapping(bytes32 => bool) public existingIDs;
event DAppCreated(bytes32 indexed id, uint newEffectiveBalance);
event Upvote(bytes32 indexed id, uint newEffectiveBalance);

View File

@ -0,0 +1,24 @@
import EmbarkJS from '../../embarkArtifacts/embarkjs'
class BlockchainService {
constructor(sharedContext, contractAddress, Validator) {
this.contract = contractAddress
this.sharedContext = sharedContext
this.validator = new Validator(this)
}
async __unlockServiceAccount() {
try {
const accounts = await EmbarkJS.Blockchain.Providers.web3.getAccounts()
if (accounts.length > 0) {
this.sharedContext.account = accounts[0]
}
this.sharedContext.account = (await EmbarkJS.enableEthereum())[0]
} catch (error) {
throw new Error('Could not unlock an account or web3 is missing')
}
}
}
export default BlockchainService

View File

@ -1,21 +1,22 @@
import BlockchainService from '../blockchain-service'
import DiscoverValidator from './discover-validator'
import DiscoverContract from '../../../embarkArtifacts/contracts/Discover'
import DiscoverServiceValidator from './discover-validator'
class DiscoverService {
constructor(Validator) {
this.validator = new Validator(this)
// TODO: Validators ? - YUP
// TODO: check for unlocked account: If it is not -> request unlocking - YUP
// TODO: Make transfer failed an Error object ?
if (!(this.validator instanceof DiscoverServiceValidator)) {
throw new Error(
'Discover Service Validator should be an instance of DiscoverValidator',
)
}
class DiscoverService extends BlockchainService {
constructor(sharedContext) {
super(sharedContext, DiscoverContract.address, DiscoverValidator)
}
// TODO: Amount -> string/bigInt/number ?
// TODO: Maybe we can get id from a DApp name ?
// TODO: formatBigNumberToNumber
// TODO: validators - YUP
// View methods
async upVoteEffect(id, amount) {
const dapp = await this.getDAppById(id)
await this.validator.validateUpVoteEffect(dapp, id, amount)
@ -31,17 +32,82 @@ class DiscoverService {
}
async getDAppById(id) {
const dappId = await DiscoverContract.methods.id2index(id).call()
return DiscoverContract.methods.dapps(dappId).call()
try {
const dappId = await DiscoverContract.methods.id2index(id).call()
const dapp = await DiscoverContract.methods.dapps(dappId).call()
return dapp
} catch (error) {
throw new Error('Searching DApp does not exists')
}
}
async safeMax() {
return DiscoverContract.methods.safeMax().call()
}
// async isDAppExists(id) {
// return DiscoverContract.methods.existingIDs(id).call()
// }
async isDAppExists(id) {
return DiscoverContract.methods.existingIDs(id).call()
}
// Transaction methods
async createDApp(id, amount, metadata) {
await this.validator.validateDAppCreation(id, amount)
const callData = DiscoverContract.methods
.createDApp(id, amount, metadata)
.encodeABI()
await this.sharedContext.SNTService.approveAndCall(
this.contract,
amount,
callData,
)
}
async upVote(id, amount) {
await this.validator.validateUpVoting(id, amount)
const callData = DiscoverContract.methods.upvote(id, amount).encodeABI()
await this.sharedContext.SNTService.approveAndCall(
this.contract,
amount,
callData,
)
}
async downVote(id, amount) {
await this.validator.validateDownVoting(id, amount)
const callData = DiscoverContract.methods.downvote(id, amount).encodeABI()
await this.sharedContext.SNTService.approveAndCall(
this.contract,
amount,
callData,
)
}
async withdraw(id, amount) {
await super.__unlockServiceAccount(this.service)
await this.validator.validateWithdrawing(id, amount)
try {
await DiscoverContract.methods
.withdraw(id, amount)
.send({ from: this.sharedContext.account })
} catch (error) {
throw new Error('Transfer on withdraw failed')
}
}
async setMetadata(id, metadata) {
await super.__unlockServiceAccount(this.service)
await this.validator.validateMetadataSet(id)
await DiscoverContract.methods
.setMetadata(id, metadata)
.send({ from: this.sharedContext.account })
}
}
export default DiscoverService

View File

@ -1,14 +0,0 @@
const discoverValidatorUtils = {
async checkDappCorrectness(dapp, id) {
if (dapp.id != id) {
throw new Error('Error fetching correct data')
}
},
async checkUpVotingAmount(amount, limit) {
if (amount > limit) {
throw new Error('You cannot upvote by this much, try with a lower amount')
}
},
}
export default discoverValidatorUtils

View File

@ -1,11 +1,87 @@
const checkDappCorrectness = async function(dapp, id) {
if (dapp.id != id) {
throw new Error('Error fetching correct data')
}
}
class DiscoverValidator {
constructor(service) {
this.service = service
}
async validateUpVoteEffect(id, amount) {}
async validateUpVoteEffect(id, amount) {
const dapp = await this.service.getDAppById(id)
await checkDappCorrectness(dapp, id)
async validateDownVoteCost(id) {}
// TODO: should check if dapp.balance is a big number
const safeMax = await this.service.safeMax()
if (dapp.balance + amount > safeMax) {
throw new Error('You cannot upvote by this much, try with a lower amount')
}
}
async validateDownVoteCost(id) {
const dapp = await this.service.getDAppById(id)
await checkDappCorrectness(dapp, id)
}
async validateDAppCreation(id, amount) {
const dappExists = await this.service.isDAppExists(id)
if (dappExists) {
throw new Error('You must submit a unique ID')
}
if (amount <= 0) {
throw new Error(
'You must spend some SNT to submit a ranking in order to avoid spam',
)
}
const safeMax = await this.service.safeMax()
if (amount > safeMax) {
throw new Error('You cannot stake more SNT than the ceiling dictates')
}
}
async validateUpVoting(id, amount) {
await this.validateUpVoteEffect(id, amount)
if (amount <= 0) {
throw new Error('You must send some SNT in order to upvote')
}
}
async validateDownVoting(id, amount) {
await this.validateDownVoteCost(id)
const downVoteCost = await this.service.downVoteCost(id)
if (downVoteCost != amount) {
throw new Error('Incorrect amount: valid iff effect on ranking is 1%')
}
}
async validateWithdrawing(id, amount) {
const dapp = await this.service.getDAppById(id)
await checkDappCorrectness(dapp, id)
if (dapp.developer != this.service.sharedContext.account) {
throw new Error('Only the developer can withdraw SNT staked on this data')
}
if (amount > dapp.available) {
throw new Error(
'You can only withdraw a percentage of the SNT staked, less what you have already received',
)
}
}
async validateMetadataSet(id) {
const dapp = await this.service.getDAppById(id)
if (dapp.developer != this.service.sharedContext.account) {
throw new Error('Only the developer can update the metadata')
}
}
}
export default DiscoverValidator

View File

@ -1,10 +0,0 @@
import DiscoverService from '../discover-service'
import DiscoverReadServiceValidator from './validator'
class DiscoverReadService extends DiscoverService {
constructor() {
super(DiscoverReadServiceValidator)
}
}
export default DiscoverReadService

View File

@ -1,23 +0,0 @@
import DiscoverValidator from '../discover-validator'
import DiscoverValidatorUtils from '../discover-validator-utils'
class DiscoverReadServiceValidator extends DiscoverValidator {
async validateUpVoteEffect(id, amount) {
const dapp = await this.service.getDAppById()
await DiscoverValidatorUtils.checkDappCorrectness(dapp, id)
// TODO: should check if dapp.balance is a big number
const safeMax = await this.service.safeMax()
await DiscoverValidatorUtils.checkUpVotingAmount(
dapp.balance + amount,
safeMax,
)
}
async validateDownVoteCost(id) {
const dapp = await this.service.getDAppById()
await DiscoverValidatorUtils.checkDappCorrectness(dapp, id)
}
}
export default DiscoverReadServiceValidator

View File

@ -1,88 +0,0 @@
import DiscoverContract from '../../../../embarkArtifacts/contracts/Discover'
import DiscoverService from '../discover-service'
import DiscoverWServiceValidator from './validator'
// TODO: Validators ?
// TODO: check for unlocked account: If it is not -> request unlocking
// TODO: preOperation -> inherited method ?
const unlockAccount = async function(account) {
return account
}
class DiscoverWriteService extends DiscoverService {
constructor(unlockedAccount) {
super(DiscoverWServiceValidator)
this.account = unlockedAccount
}
async createDApp(id, amount, metadata) {
await unlockAccount(this.account)
await this.validator.validateDAppCreation(id, amount)
try {
await DiscoverContract.methods
.createDApp(id, amount, metadata)
.send({ from: this.account })
} catch (error) {
console.log(error)
throw new Error('Transfer failed')
}
}
async upVote(id, amount) {
await unlockAccount(this.account)
await this.validator.validateUpVoting(id, amount)
try {
await DiscoverContract.methods
.upVote(id, amount)
.send({ from: this.account })
} catch (error) {
throw new Error('Transfer failed')
}
}
async downVote(id, amount) {
await unlockAccount(this.account)
await this.validator.validateDownVoting(id, amount)
try {
await DiscoverContract.methods
.downVote(id, amount)
.send({ from: this.account })
} catch (error) {
throw new Error('Transfer failed')
}
}
async withdraw(id, amount) {
await unlockAccount(this.account)
await this.validator.validateWithdrawing(id, amount)
try {
await DiscoverContract.methods
.withdraw(id, amount)
.send({ from: this.account })
} catch (error) {
throw new Error('Transfer failed')
}
}
async setMetadata(id, metadata) {
await unlockAccount(this.account)
await this.validator.validateMetadataSet(id)
await DiscoverContract.methods
.setMetadata(id, metadata)
.send({ from: this.account })
}
// async receiveApproval(from, amount, token, data) {
// await unlockAccount(this.account);
// await this.validator.validateReceiveApproval();
// await DiscoverContract.methods.receiveApproval(from, amount, token, data, { from: this.account });
// }
}
export default DiscoverWriteService

View File

@ -1,71 +0,0 @@
import DiscoverValidatorUtils from '../discover-validator-utils'
import DiscoverRServiceValidator from '../read-service/validator'
class DiscoverWriteServiceValidator extends DiscoverRServiceValidator {
// TODO: Add SNT allowance checks
async validateDAppCreation(id, amount) {
// const dappExists = await this.service.isDAppExists(id)
// if (dappExists) {
// throw new Error('You must submit a unique ID')
// }
if (amount <= 0) {
throw new Error(
'You must spend some SNT to submit a ranking in order to avoid spam',
)
}
const safeMax = await this.service.safeMax()
if (amount > safeMax) {
throw new Error('You cannot stake more SNT than the ceiling dictates')
}
}
// TODO: Add SNT allowance checks
async validateUpVoting(id, amount) {
await super.validateUpVoteEffect(id, amount)
if (amount <= 0) {
throw new Error('You must send some SNT in order to upvote')
}
}
// TODO: Add SNT allowance checks
async validateDownVoting(id, amount) {
await super.validateDownVoteCost(id)
const downVoteCost = await this.service.downVoteCost(id)
if (downVoteCost != amount) {
throw new Error('Incorrect amount: valid iff effect on ranking is 1%')
}
}
async validateWithdrawing(id, amount) {
const dapp = await this.service.getDAppById(id)
await DiscoverValidatorUtils.checkDappCorrectness(dapp, id)
if (dapp.developer != this.service.account) {
throw new Error('Only the developer can withdraw SNT staked on this data')
}
if (amount > dapp.available) {
throw new Error(
'You can only withdraw a percentage of the SNT staked, less what you have already received',
)
}
}
async validateMetadataSet(id) {
const dapp = await this.service.getDAppById(id)
if (dapp.developer != this.service.account) {
throw new Error('Only the developer can update the metadata')
}
}
// async validateReceiveApproval() {
// }
}
export default DiscoverWriteServiceValidator

View File

@ -1,27 +1,22 @@
// import DiscoverContract from '../../../embarkArtifacts/contracts/Discover';
import EmbarkJS from '../../embarkArtifacts/embarkjs'
import SNTService from './snt-services/snt-service'
import DiscoverService from './discover-services/discover-service'
import DiscoverReadService from './discover-services/read-service/discover-r-service'
import DiscoverWriteService from './discover-services/write-service/discover-w-service'
const ReadOnlyServices = {
DiscoverService: new DiscoverReadService(),
}
// TODO: ask Andy what kind of wallets is going to be used
const init = async function() {
try {
const account = (await EmbarkJS.enableEthereum())[0]
const sharedContext = {
account: '',
}
const DiscoverService = new DiscoverWriteService(account)
sharedContext.SNTService = new SNTService(sharedContext)
sharedContext.DiscoverService = new DiscoverService(sharedContext)
return {
DiscoverService,
SNTService: sharedContext.SNTService,
DiscoverService: sharedContext.DiscoverService,
}
} catch (error) {
// TODO: Should handle it in an elegant way
throw new Error(error.message)
}
}
export default { init, ...ReadOnlyServices }
export default { init }

View File

@ -0,0 +1,46 @@
import BlockchainService from '../blockchain-service'
import SNTValidator from './snt-validator'
import SNTToken from '../../../embarkArtifacts/contracts/SNT'
class SNTService extends BlockchainService {
constructor(sharedContext) {
super(sharedContext, SNTToken.address, SNTValidator)
}
async allowance(from, to) {
return SNTToken.methods.allowance(from, to).call()
}
async balanceOf(account) {
return SNTToken.methods.balanceOf(account).call()
}
async controller() {
return SNTToken.methods.controller().call()
}
async transferable() {
return SNTToken.methods.transfersEnabled().call()
}
async approveAndCall(spender, amount, callData) {
await super.__unlockServiceAccount(this.service)
await this.validator.validateApproveAndCall(spender, amount)
await SNTToken.methods
.approveAndCall(spender, amount, callData)
.send({ from: this.sharedContext.account })
}
// This is for testing purpose only
async generateTokens() {
await super.__unlockServiceAccount(this.service)
await SNTToken.methods
.generateTokens(this.sharedContext.account, 10000)
.send()
}
}
export default SNTService

View File

@ -0,0 +1,35 @@
class SNTValidator {
constructor(service) {
this.service = service
}
async validateSNTTransferFrom(amount) {
const toBalance = await this.service.balanceOf(
this.service.sharedContext.account,
)
if (toBalance < amount) {
throw new Error('Not enough SNT balance')
}
}
async validateApproveAndCall(spender, amount) {
const isTransferableToken = await this.service.transferable()
if (!isTransferableToken) {
throw new Error('Token is not transferable')
}
await this.validateSNTTransferFrom(amount)
const allowance = await this.service.allowance(
this.service.sharedContext.account,
spender,
)
if (amount != 0 && allowance != 0) {
throw new Error('You have allowance already')
}
}
}
export default SNTValidator

View File

@ -4,13 +4,11 @@ import BlockchainSDK from '../../common/blockchain'
class Example extends React.Component {
async logDiscoverMethod() {
const services = await BlockchainSDK.init()
console.log(
await services.DiscoverService.createDApp(
'0x123',
'100000000000000000',
'0x123',
),
)
console.log(await services.SNTService.controller())
// await services.SNTService.generateTokens()
// await services.DiscoverService.createDApp('0x2', 10000, '0x2')
// console.log(await services.DiscoverService.getDAppById('0x2'))
}
render() {