commit
						7636e08e8f
					
				| @ -1,17 +0,0 @@ | ||||
| export default class Base { | ||||
|   constructor(contract) { | ||||
|     this.contract = contract; | ||||
|   } | ||||
| 
 | ||||
|   get functions() { | ||||
|     return this.contract.functions; | ||||
|   } | ||||
| 
 | ||||
|   on(type, callback) { | ||||
|     let eventMethod = `on${type.toLowerCase()}`; | ||||
|     // Don't use this.contract.events here. Seems to be a bug in ethers.js
 | ||||
|     this.contract[eventMethod] = callback; | ||||
| 
 | ||||
|     return this; | ||||
|   } | ||||
| } | ||||
| @ -1,61 +0,0 @@ | ||||
| import ethers from 'ethers'; | ||||
| import RSVP from 'rsvp'; | ||||
| 
 | ||||
| import Kredits from '../kredits'; | ||||
| import ContributorSerializer from '../serializers/contributor'; | ||||
| 
 | ||||
| import Base from './base'; | ||||
| 
 | ||||
| export default class Contributor extends Base { | ||||
|   all() { | ||||
|     return this.functions.contributorsCount() | ||||
|       .then((count) => { | ||||
|         count = count.toNumber(); | ||||
|         let contributors = []; | ||||
| 
 | ||||
|         for (let id = 1; id <= count; id++) { | ||||
|           contributors.push(this.getById(id)); | ||||
|         } | ||||
| 
 | ||||
|         return RSVP.all(contributors); | ||||
|       }); | ||||
|   } | ||||
| 
 | ||||
|   getById(id) { | ||||
|     id = ethers.utils.bigNumberify(id); | ||||
| 
 | ||||
|     return this.functions.getContributorById(id) | ||||
|       .then((data) => { | ||||
|         // TODO: remove as soon as the contract provides the id
 | ||||
|         data.id = id; | ||||
|         // TODO: rename address to account
 | ||||
|         data.address = data.account; | ||||
| 
 | ||||
|         return data; | ||||
|       }) | ||||
|       // Fetch IPFS data if available
 | ||||
|       .then((data) => { | ||||
|         return Kredits.ipfs.catAndMerge(data, ContributorSerializer.deserialize); | ||||
|       }); | ||||
|   } | ||||
| 
 | ||||
|   add(contributorAttr) { | ||||
|     let json = ContributorSerializer.serialize(contributorAttr); | ||||
|     // TODO: validate against schema
 | ||||
| 
 | ||||
|     return Kredits.ipfs | ||||
|       .add(json) | ||||
|       .then((ipfsHashAttr) => { | ||||
|         let contributor = [ | ||||
|           contributorAttr.address, | ||||
|           ipfsHashAttr.ipfsHash, | ||||
|           ipfsHashAttr.hashFunction, | ||||
|           ipfsHashAttr.hashSize, | ||||
|           contributorAttr.isCore, | ||||
|         ]; | ||||
| 
 | ||||
|         console.log('[kredits] addContributor', ...contributor); | ||||
|         return this.functions.addContributor(...contributor); | ||||
|       }); | ||||
|   } | ||||
| } | ||||
| @ -1,9 +0,0 @@ | ||||
| import Contributor from './contributor'; | ||||
| import Operator from './operator'; | ||||
| import Token from './token'; | ||||
| 
 | ||||
| export default { | ||||
|   Contributors: Contributor, | ||||
|   Operator, | ||||
|   Token | ||||
| }; | ||||
| @ -1,61 +0,0 @@ | ||||
| import ethers from 'ethers'; | ||||
| import RSVP from 'rsvp'; | ||||
| 
 | ||||
| import Kredits from '../kredits'; | ||||
| import ContributionSerializer from '../serializers/contribution'; | ||||
| 
 | ||||
| import Base from './base'; | ||||
| 
 | ||||
| export default class Operator extends Base { | ||||
|   all() { | ||||
|     return this.functions.proposalsCount() | ||||
|       .then((count) => { | ||||
|         count = count.toNumber(); | ||||
|         let proposals = []; | ||||
| 
 | ||||
|         for (let id = 1; id <= count; id++) { | ||||
|           proposals.push(this.getById(id)); | ||||
|         } | ||||
| 
 | ||||
|         return RSVP.all(proposals); | ||||
|       }); | ||||
|   } | ||||
| 
 | ||||
|   getById(id) { | ||||
|     id = ethers.utils.bigNumberify(id); | ||||
| 
 | ||||
|     return this.functions.proposals(id) | ||||
|       .then((data) => { | ||||
|         // TODO: remove as soon as the contract provides the id
 | ||||
|         data.id = id; | ||||
|         // TODO: rename creatorAddress to creator
 | ||||
|         data.creatorAddress = data.creator; | ||||
| 
 | ||||
|         return data; | ||||
|       }) | ||||
|       // Fetch IPFS data if available
 | ||||
|       .then((data) => { | ||||
|         return Kredits.ipfs.catAndMerge(data, ContributionSerializer.deserialize); | ||||
|       }); | ||||
|   } | ||||
| 
 | ||||
|   addProposal(proposalAttr) { | ||||
|     let json = ContributionSerializer.serialize(proposalAttr); | ||||
|     // TODO: validate against schema
 | ||||
| 
 | ||||
|     return Kredits.ipfs | ||||
|       .add(json) | ||||
|       .then((ipfsHashAttr) => { | ||||
|         let proposal = [ | ||||
|           proposalAttr.contributorId, | ||||
|           proposalAttr.amount, | ||||
|           ipfsHashAttr.ipfsHash, | ||||
|           ipfsHashAttr.hashFunction, | ||||
|           ipfsHashAttr.hashSize, | ||||
|         ]; | ||||
| 
 | ||||
|         console.log('[kredits] addProposal', ...proposal); | ||||
|         return this.functions.addProposal(...proposal); | ||||
|       }); | ||||
|   } | ||||
| } | ||||
| @ -1,4 +0,0 @@ | ||||
| import Base from './base'; | ||||
| 
 | ||||
| export default class Token extends Base { | ||||
| } | ||||
| @ -1,2 +0,0 @@ | ||||
| import kredits from './kredits'; | ||||
| export default kredits; | ||||
| @ -1,102 +0,0 @@ | ||||
| import ethers from 'npm:ethers'; | ||||
| import RSVP from 'rsvp'; | ||||
| 
 | ||||
| import abis from 'contracts/abis'; | ||||
| import addresses from 'contracts/addresses'; | ||||
| 
 | ||||
| import contracts from './contracts'; | ||||
| import IPFS from './utils/ipfs'; | ||||
| 
 | ||||
| // Helpers
 | ||||
| function capitalize(word) { | ||||
|   let [first, ...rest] = word; | ||||
|   return `${first.toUpperCase()}${rest.join('')}`; | ||||
| } | ||||
| 
 | ||||
| export default class Kredits { | ||||
|   constructor(provider, signer, addresses) { | ||||
|     this.provider = provider; | ||||
|     this.signer = signer; | ||||
| 
 | ||||
|     // Initialize our registry contract
 | ||||
|     this.addresses = addresses; | ||||
|     this.contracts = {}; | ||||
|   } | ||||
| 
 | ||||
|   static setup(provider, signer, ipfsConfig) { | ||||
|     this.ipfsConfig = ipfsConfig; | ||||
|     this.ipfs = new IPFS(ipfsConfig); | ||||
| 
 | ||||
|     return this.ipfs._ipfsAPI.id().catch((error) => { | ||||
|       throw new Error(`IPFS node not available; config: ${JSON.stringify(ipfsConfig)} - ${error.message}`); | ||||
|     }).then(() => { | ||||
| 
 | ||||
|       let registryContract = this.initRegistryContract(provider); | ||||
| 
 | ||||
|       let addresses = Object.keys(contracts).reduce((mem, name) => { | ||||
|         let contractName = capitalize(name); | ||||
|         mem[contractName] = registryContract.functions.getProxyFor(contractName).catch((error) => { | ||||
|           throw new Error(`Failed to get address for ${contractName} from registry at ${registryContract.address} | ||||
|             - correct registry? does it have version entry? - ${error.message}` | ||||
|           ); | ||||
|         }); | ||||
|         return mem; | ||||
|       }, {}); | ||||
| 
 | ||||
|       return RSVP.hash(addresses) | ||||
|         .then((addresses) => { | ||||
|           return new Kredits(provider, signer, addresses); | ||||
|         }); | ||||
|     }); | ||||
|   } | ||||
| 
 | ||||
|   static initRegistryContract(provider) { | ||||
|     let address = addresses['Registry'][provider.chainId]; | ||||
|     if (!address) { | ||||
|       throw new Error(`Registry address not found; invalid network?
 | ||||
|         requested network: ${provider.chainId} | ||||
|         supported networks: ${Object.keys(addresses['Registry'])} | ||||
|       `);
 | ||||
|     } | ||||
|     provider.getCode(address).then((code) => { | ||||
|       // not sure if we always get the same return value of the code is not available
 | ||||
|       // that's why checking if it is < 5 long
 | ||||
|       if (code === '0x00' || code.length < 5) { | ||||
|         throw new Error(`Registry not found at ${address} on network ${provider.chainId}`); | ||||
|       } | ||||
|     }); | ||||
|     let abi = abis['Registry']; | ||||
|     console.log('Initialize registry contract:', address, abi, provider); | ||||
|     return new ethers.Contract(address, abi, provider); | ||||
|   } | ||||
| 
 | ||||
|   get Contributor() { | ||||
|     // TODO: rename to contributor
 | ||||
|     return this.contractFor('contributors'); | ||||
|   } | ||||
| 
 | ||||
|   get Operator() { | ||||
|     return this.contractFor('operator'); | ||||
|   } | ||||
| 
 | ||||
|   get Token() { | ||||
|     return this.contractFor('token'); | ||||
|   } | ||||
| 
 | ||||
|   // Should be private
 | ||||
|   contractFor(name) { | ||||
|     if (this.contracts[name]) { | ||||
|       return this.contracts[name]; | ||||
|     } | ||||
| 
 | ||||
|     let contractName = capitalize(name); | ||||
|     let address = this.addresses[contractName]; | ||||
|     if (!address || !abis[contractName]) { | ||||
|       throw new Error(`Address or ABI not found for ${contractName}`); | ||||
|     } | ||||
|     let contract = new ethers.Contract(address, abis[contractName], this.signer); | ||||
|     this.contracts[name] = new contracts[contractName](contract); | ||||
| 
 | ||||
|     return this.contracts[name]; | ||||
|   } | ||||
| } | ||||
| @ -1,64 +0,0 @@ | ||||
| /** | ||||
|  * Handle serialization for JSON-LD object of the contribution, according to | ||||
|  * https://github.com/67P/kosmos-schemas/blob/master/schemas/contribution.json
 | ||||
|  * | ||||
|  * @class | ||||
|  * @public | ||||
|  */ | ||||
| export default class Contributor { | ||||
|  /** | ||||
|   * Deserialize JSON to object | ||||
|   * | ||||
|   * @method | ||||
|   * @public | ||||
|   */ | ||||
|   static deserialize(serialized) { | ||||
|     let { | ||||
|       kind, | ||||
|       description, | ||||
|       details, | ||||
|       url, | ||||
|     } = JSON.parse(serialized.toString('utf8')); | ||||
| 
 | ||||
|     return { | ||||
|       kind, | ||||
|       description, | ||||
|       details, | ||||
|       url, | ||||
|       ipfsData: serialized, | ||||
|     }; | ||||
|   } | ||||
| 
 | ||||
|  /** | ||||
|   * Serialize object to JSON | ||||
|   * | ||||
|   * @method | ||||
|   * @public | ||||
|   */ | ||||
|   static serialize(deserialized) { | ||||
|     let { | ||||
|       contributorIpfsHash, | ||||
|       kind, | ||||
|       description, | ||||
|       url, | ||||
|     } = deserialized; | ||||
| 
 | ||||
|     let data = { | ||||
|       "@context": "https://schema.kosmos.org", | ||||
|       "@type": "Contribution", | ||||
|       "contributor": { | ||||
|         "ipfs": contributorIpfsHash | ||||
|       }, | ||||
|       kind, | ||||
|       description, | ||||
|       "details": {} | ||||
|     }; | ||||
| 
 | ||||
|     if (url) { | ||||
|       data["url"] = url; | ||||
|     } | ||||
| 
 | ||||
|     // Write it pretty to ipfs
 | ||||
|     return JSON.stringify(data, null, 2); | ||||
|   } | ||||
| } | ||||
| @ -1,93 +0,0 @@ | ||||
| /** | ||||
|  * Handle serialization for JSON-LD object of the contributor, according to | ||||
|  * https://github.com/67P/kosmos-schemas/blob/master/schemas/contributor.json
 | ||||
|  * | ||||
|  * @class | ||||
|  * @public | ||||
|  */ | ||||
| export default class Contributor { | ||||
|  /** | ||||
|   * Deserialize JSON to object | ||||
|   * | ||||
|   * @method | ||||
|   * @public | ||||
|   */ | ||||
|   static deserialize(serialized) { | ||||
|     let { | ||||
|       name, | ||||
|       kind, | ||||
|       url, | ||||
|       accounts, | ||||
|     } = JSON.parse(serialized.toString('utf8')); | ||||
| 
 | ||||
|     let github_username, github_uid, wiki_username; | ||||
|     let github = accounts.find((a) => a.site === 'github.com'); | ||||
|     let wiki   = accounts.find((a) => a.site === 'wiki.kosmos.org'); | ||||
| 
 | ||||
|     if (github) { | ||||
|       ({ username: github_username, uid: github_uid} = github); | ||||
|     } | ||||
|     if (wiki) { | ||||
|       ({ username: wiki_username } = wiki); | ||||
|     } | ||||
| 
 | ||||
|     return { | ||||
|       name, | ||||
|       kind, | ||||
|       url, | ||||
|       github_uid, | ||||
|       github_username, | ||||
|       wiki_username, | ||||
|       ipfsData: serialized, | ||||
|     }; | ||||
|   } | ||||
| 
 | ||||
|  /** | ||||
|   * Serialize object to JSON | ||||
|   * | ||||
|   * @method | ||||
|   * @public | ||||
|   */ | ||||
|   static serialize(deserialized) { | ||||
|     let { | ||||
|       name, | ||||
|       kind, | ||||
|       url, | ||||
|       github_uid, | ||||
|       github_username, | ||||
|       wiki_username, | ||||
|     } = deserialized; | ||||
| 
 | ||||
|     let data = { | ||||
|       "@context": "https://schema.kosmos.org", | ||||
|       "@type": "Contributor", | ||||
|       kind, | ||||
|       name, | ||||
|       "accounts": [] | ||||
|     }; | ||||
| 
 | ||||
|     if (url) { | ||||
|       data["url"] = url; | ||||
|     } | ||||
| 
 | ||||
|     if (github_uid) { | ||||
|       data.accounts.push({ | ||||
|         "site": "github.com", | ||||
|         "uid": github_uid, | ||||
|         "username": github_username, | ||||
|         "url": `https://github.com/${github_username}` | ||||
|       }); | ||||
|     } | ||||
| 
 | ||||
|     if (wiki_username) { | ||||
|       data.accounts.push({ | ||||
|         "site": "wiki.kosmos.org", | ||||
|         "username": wiki_username, | ||||
|         "url": `https://wiki.kosmos.org/User:${wiki_username}` | ||||
|       }); | ||||
|     } | ||||
| 
 | ||||
|     // Write it pretty to ipfs
 | ||||
|     return JSON.stringify(data, null, 2); | ||||
|   } | ||||
| } | ||||
| @ -1,54 +0,0 @@ | ||||
| import ipfsAPI from 'ipfs-api'; | ||||
| import multihashes from 'multihashes'; | ||||
| 
 | ||||
| export default class IPFS { | ||||
| 
 | ||||
|   constructor(config) { | ||||
|     this._ipfsAPI = ipfsAPI(config); | ||||
|     this._config = config; | ||||
|   } | ||||
| 
 | ||||
|   catAndMerge(data, deserialize) { | ||||
|     // if no hash details are found simply return the data; nothing to merge
 | ||||
|     if (!data.hashSize || data.hashSize === 0) { | ||||
|       return data; | ||||
|     } | ||||
|     return this.cat(data) | ||||
|       .then(deserialize) | ||||
|       .then((attributes) => { | ||||
|         return Object.assign({}, data, attributes); | ||||
|       }); | ||||
|   } | ||||
| 
 | ||||
|   add(data) { | ||||
|     return this._ipfsAPI | ||||
|       .add(new this._ipfsAPI.Buffer(data)) | ||||
|       .then((res) => { | ||||
|         return this.decodeHash(res[0].hash); | ||||
|       }); | ||||
|   } | ||||
| 
 | ||||
|   cat(hashData) { | ||||
|     let ipfsHash = hashData; // default - if it is a string
 | ||||
|     if (hashData.hasOwnProperty('hashSize')) { | ||||
|       ipfsHash = this.encodeHash(hashData); | ||||
|     } | ||||
|     return this._ipfsAPI.cat(ipfsHash); | ||||
|   } | ||||
| 
 | ||||
|   decodeHash(ipfsHash) { | ||||
|     let multihash = multihashes.decode(multihashes.fromB58String(ipfsHash)); | ||||
|     return { | ||||
|       ipfsHash: '0x' + multihashes.toHexString(multihash.digest), | ||||
|       hashSize: multihash.length, | ||||
|       hashFunction: multihash.code, | ||||
|       sourceHash: ipfsHash | ||||
|     }; | ||||
|   } | ||||
| 
 | ||||
|   encodeHash(hashData) { | ||||
|     let digest = this._ipfsAPI.Buffer.from(hashData.ipfsHash.slice(2), 'hex'); | ||||
|     return multihashes.encode(digest, hashData.hashFunction, hashData.hashSize); | ||||
|   } | ||||
| 
 | ||||
| } | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user