Add IPFS data uploading

This commit is contained in:
Lyubomir Kiprov 2019-05-03 18:50:54 +03:00
parent a26091f59e
commit c3920055bb
18 changed files with 217 additions and 72 deletions

1
.gitignore vendored
View File

@ -1,5 +1,6 @@
.embark
chains.json
config/development/mnemonic
config/livenet/password
config/production/password
coverage

View File

@ -29,11 +29,6 @@ module.exports = {
// 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
// },

View File

@ -1,3 +1,5 @@
const wallet = require('./development/mnemonic')
module.exports = {
// default applies to all environments
default: {
@ -31,9 +33,8 @@ module.exports = {
accounts: [
{
privateKey:
'0xEFA9DB87A755C9D2B96F77BBCB9EF06CBDDFC01DB1A5129CE2649F73E9C2739C',
balance: '100 ether',
mnemonic: wallet.mnemonic,
balance: '1534983463450 ether',
},
],
},
@ -72,7 +73,6 @@ module.exports = {
TestBancorFormula: { deploy: false },
MiniMeTokenFactory: {},
SNT: {
from: '0x68C864373C6631984B646453138557A81224ACf6',
instanceOf: 'MiniMeToken',
args: [
'$MiniMeTokenFactory',
@ -85,7 +85,6 @@ module.exports = {
],
},
Discover: {
from: '0x68C864373C6631984B646453138557A81224ACf6',
args: ['$SNT'],
},
},

View File

@ -0,0 +1,2 @@
module.exports.mnemonic =
'artefact rebuild liquid honey sport clean candy motor cereal job gap series'

View File

@ -237,6 +237,15 @@ contract Discover is ApproveAndCallFallBack, BancorFormula {
return (mEBalance.sub(d.effectiveBalance));
}
/**
* @dev Used in UI in order to fetch all dapps
* @return dapps count
*/
function getDAppsCount() external view returns(uint) {
return dapps.length;
}
/**
* @dev Downvotes always remove 1% of the current ranking.
* @param _id bytes32 unique identifier.

View File

@ -1,24 +0,0 @@
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,8 +1,13 @@
import SNTService from './snt-services/snt-service'
import DiscoverService from './discover-services/discover-service'
import utils from './utils'
import SNTService from './sdk/snt-services/snt-service'
import DiscoverService from './sdk/discover-services/discover-service'
const init = async function() {
import BlockchainConfig from './sdk/config'
const init = function() {
try {
BlockchainConfig()
const sharedContext = {
account: '',
}
@ -13,10 +18,11 @@ const init = async function() {
return {
SNTService: sharedContext.SNTService,
DiscoverService: sharedContext.DiscoverService,
utils,
}
} catch (error) {
throw new Error(error.message)
}
}
export default { init }
export default { init, utils }

View File

@ -0,0 +1,11 @@
export const base64ToBlob = base64Text => {
const byteString = atob(base64Text.split(',')[1])
const arrayBuffer = new ArrayBuffer(byteString.length)
const uintArray = new Uint8Array(arrayBuffer)
for (let i = 0; i < byteString.length; i++) {
uintArray[i] = byteString.charCodeAt(i)
}
return new Blob([arrayBuffer])
}

View File

@ -0,0 +1,50 @@
import { base64ToBlob } from './helpers'
// Todo: EmbarkJS -> setup it in init
// Todo: Should check for isAvailable
import EmbarkJS from '../../../embarkArtifacts/embarkjs'
EmbarkJS.Storage.setProvider('ipfs')
export const uploadMetadata = async metadata => {
try {
const hash = await EmbarkJS.Storage.saveText(metadata)
return hash
} catch (error) {
throw new Error(
`Uploading DApp metadata to IPFS failed. Details: ${error.message}`,
)
}
}
// Todo: should convert base64 image into binary data in order to upload it on IPFS
export const uploadImage = async base64Image => {
try {
const imageFile = [
{
files: [base64ToBlob(base64Image)],
},
]
const hash = await EmbarkJS.Storage.uploadFile(imageFile)
return hash
} catch (error) {
throw new Error(
`Uploading DApp image to IPFS failed. Details: ${error.message}`,
)
}
}
export const retrieveMetadata = async metadataHash => {
try {
const metadata = await EmbarkJS.Storage.get(metadataHash)
return metadata
} catch (error) {
throw new Error(
`Fetching metadata from IPFS failed. Details: ${error.message}`,
)
}
}
export const retrieveImageUrl = async imageHash => {
return EmbarkJS.Storage.getUrl(imageHash)
}

View File

@ -0,0 +1,31 @@
import EmbarkJS from '../../../embarkArtifacts/embarkjs'
class BlockchainService {
constructor(sharedContext, contract, Validator) {
this.contract = contract.address
contract.setProvider(global.web3.currentProvider)
this.sharedContext = sharedContext
this.validator = new Validator(this)
}
async __unlockServiceAccount() {
const accounts = await EmbarkJS.Blockchain.Providers.web3.getAccounts()
// if (accounts.length > 0) {
this.sharedContext.account = accounts[0]
// } else {
// const provider = global.web3.currentProvider
// Check for undefined
// console.log(await global.web3.eth.getAccounts())
// const accounts = await EmbarkJS.enableEthereum()
// if (accounts) {
// this.sharedContext.account = accounts[0]
// }
// global.web3.setProvider(provider)
// }
// throw new Error('Could not unlock an account or web3 is missing')
}
}
export default BlockchainService

View File

@ -0,0 +1,8 @@
import Web3 from '../../../embarkArtifacts/modules/web3'
// Should be moved to .env
const RPC_URL = 'http://localhost:8545'
export default function() {
global.web3 = new Web3(new Web3.providers.HttpProvider(RPC_URL))
}

View File

@ -1,19 +1,17 @@
import broadcastContractFn from '../helpers'
import * as ipfsSDK from '../../ipfs'
import BlockchainService from '../blockchain-service'
import DiscoverValidator from './discover-validator'
import DiscoverContract from '../../../embarkArtifacts/contracts/Discover'
// TODO: Validators ? - YUP
// TODO: check for unlocked account: If it is not -> request unlocking - YUP
// TODO: Make transfer failed an Error object ?
import DiscoverContract from '../../../../embarkArtifacts/contracts/Discover'
class DiscoverService extends BlockchainService {
constructor(sharedContext) {
super(sharedContext, DiscoverContract.address, DiscoverValidator)
super(sharedContext, DiscoverContract, DiscoverValidator)
}
// TODO: Amount -> string/bigInt/number ?
// TODO: Maybe we can get id from a DApp name ?
// TODO: formatBigNumberToNumber
// View methods
@ -31,6 +29,16 @@ class DiscoverService extends BlockchainService {
return DiscoverContract.methods.upvoteEffect(id).call()
}
// Todo: Should be implemented
// async getDApps() {
// const dapps = []
// const dappsCount = await DiscoverContract.methods.getDAppsCount().call()
// for (let i = 0; i < dappsCount; i++) {
// const dapp = await DiscoverContract.methods.dapps(i).call()
// }
// }
async getDAppById(id) {
try {
const dappId = await DiscoverContract.methods.id2index(id).call()
@ -51,14 +59,22 @@ class DiscoverService extends BlockchainService {
}
// Transaction methods
async createDApp(id, amount, metadata) {
await this.validator.validateDAppCreation(id, amount)
async createDApp(amount, metadata) {
const dappMetadata = JSON.parse(JSON.stringify(metadata))
const dappId = global.web3.keccak256(JSON.stringify(dappMetadata))
await this.validator.validateDAppCreation(dappId, amount)
dappMetadata.image = await ipfsSDK.uploadImage(dappMetadata.image)
const metadataHash = await ipfsSDK.uploadMetadata(
JSON.stringify(dappMetadata),
)
const callData = DiscoverContract.methods
.createDApp(id, amount, metadata)
.createDApp(dappId, amount, metadataHash)
.encodeABI()
await this.sharedContext.SNTService.approveAndCall(
return this.sharedContext.SNTService.approveAndCall(
this.contract,
amount,
callData,
@ -69,7 +85,7 @@ class DiscoverService extends BlockchainService {
await this.validator.validateUpVoting(id, amount)
const callData = DiscoverContract.methods.upvote(id, amount).encodeABI()
await this.sharedContext.SNTService.approveAndCall(
return this.sharedContext.SNTService.approveAndCall(
this.contract,
amount,
callData,
@ -80,7 +96,7 @@ class DiscoverService extends BlockchainService {
await this.validator.validateDownVoting(id, amount)
const callData = DiscoverContract.methods.downvote(id, amount).encodeABI()
await this.sharedContext.SNTService.approveAndCall(
return this.sharedContext.SNTService.approveAndCall(
this.contract,
amount,
callData,
@ -88,25 +104,32 @@ class DiscoverService extends BlockchainService {
}
async withdraw(id, amount) {
await super.__unlockServiceAccount(this.service)
await super.__unlockServiceAccount()
await this.validator.validateWithdrawing(id, amount)
try {
await DiscoverContract.methods
.withdraw(id, amount)
.send({ from: this.sharedContext.account })
return broadcastContractFn(
DiscoverContract.methods.withdraw(id, amount).send,
this.sharedContext.account,
)
} catch (error) {
throw new Error('Transfer on withdraw failed')
throw new Error(`Transfer on withdraw failed. Details: ${error.message}`)
}
}
// Todo: Should we upload the metadata to IPFS
async setMetadata(id, metadata) {
await super.__unlockServiceAccount(this.service)
await super.__unlockServiceAccount()
await this.validator.validateMetadataSet(id)
await DiscoverContract.methods
.setMetadata(id, metadata)
.send({ from: this.sharedContext.account })
try {
return broadcastContractFn(
DiscoverContract.methods.setMetadata(id, metadata).send,
this.sharedContext.account,
)
} catch (error) {
throw new Error(`Uploading metadata failed. Details: ${error.message}`)
}
}
}

View File

@ -0,0 +1,9 @@
export default {
broadcastContractFn: (contractMethod, account) => {
return new Promise(resolve => {
contractMethod({ from: account }).on('transactionHash', hash => {
resolve(hash)
})
})
},
}

View File

@ -1,11 +1,13 @@
import broadcastContractFn from '../helpers'
import BlockchainService from '../blockchain-service'
import SNTValidator from './snt-validator'
import SNTToken from '../../../embarkArtifacts/contracts/SNT'
import SNTToken from '../../../../embarkArtifacts/contracts/SNT'
class SNTService extends BlockchainService {
constructor(sharedContext) {
super(sharedContext, SNTToken.address, SNTValidator)
super(sharedContext, SNTToken, SNTValidator)
}
async allowance(from, to) {
@ -25,21 +27,22 @@ class SNTService extends BlockchainService {
}
async approveAndCall(spender, amount, callData) {
await super.__unlockServiceAccount(this.service)
await super.__unlockServiceAccount()
await this.validator.validateApproveAndCall(spender, amount)
await SNTToken.methods
.approveAndCall(spender, amount, callData)
.send({ from: this.sharedContext.account })
return broadcastContractFn(
SNTToken.methods.approveAndCall(spender, amount, callData).send,
this.sharedContext.account,
)
}
// This is for testing purpose only
async generateTokens() {
await super.__unlockServiceAccount(this.service)
await super.__unlockServiceAccount()
await SNTToken.methods
.generateTokens(this.sharedContext.account, 10000)
.send()
.send({ from: this.sharedContext.account })
}
}

View File

@ -0,0 +1,18 @@
const TRANSACTION_STATUSES = {
Failed: 0,
Successful: 1,
Pending: 2,
}
export default {
getTxStatus: async txHash => {
const txReceipt = await global.web3.eth.getTransactionReceipt(txHash)
if (txReceipt) {
return txReceipt.status
? TRANSACTION_STATUSES.Successful
: TRANSACTION_STATUSES.Failed
}
return TRANSACTION_STATUSES.Pending
},
}

View File

@ -3,17 +3,21 @@ import BlockchainSDK from '../../common/blockchain'
class Example extends React.Component {
async logDiscoverMethod() {
const services = await BlockchainSDK.init()
console.log(await services.SNTService.controller())
// const services = await BlockchainSDK.init()
// 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() {
return <h1 onLoad={this.logDiscoverMethod()} />
return (
<div>
<h1 onLoad={this.logDiscoverMethod()} />
</div>
)
}
}
export default Example
// QmZGzoAEEZoFP9jYXoVfhkDqXHxVrFCSMxSU8eGQpcDNHw