From dec71c6f4f1722be613fe1a3e1bfbd3dda567819 Mon Sep 17 00:00:00 2001 From: Sebastian Kippe Date: Thu, 13 Aug 2020 00:27:40 +0200 Subject: [PATCH] Load community funds balances from mainnet Gnosis Safe --- app/components/budget-balances/component.js | 6 + app/components/budget-balances/template.hbs | 21 ++ app/routes/application.js | 6 +- app/services/community-funds.js | 32 +++ app/services/kredits.js | 2 +- app/templates/budget.hbs | 22 +- config/environment.js | 5 + package-lock.json | 266 ++++++++++++++++++ package.json | 1 + .../budget-balances/component-test.js | 26 ++ tests/unit/services/community-funds-test.js | 12 + 11 files changed, 375 insertions(+), 24 deletions(-) create mode 100644 app/components/budget-balances/component.js create mode 100644 app/components/budget-balances/template.hbs create mode 100644 app/services/community-funds.js create mode 100644 tests/integration/components/budget-balances/component-test.js create mode 100644 tests/unit/services/community-funds-test.js diff --git a/app/components/budget-balances/component.js b/app/components/budget-balances/component.js new file mode 100644 index 0000000..4c903e1 --- /dev/null +++ b/app/components/budget-balances/component.js @@ -0,0 +1,6 @@ +import Component from '@glimmer/component'; +import { inject as service } from '@ember/service'; + +export default class BudgetBalancesComponent extends Component { + @service communityFunds +} diff --git a/app/components/budget-balances/template.hbs b/app/components/budget-balances/template.hbs new file mode 100644 index 0000000..617db91 --- /dev/null +++ b/app/components/budget-balances/template.hbs @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + +
TokenAmountFiat value
WBTC{{this.communityFunds.balanceWBTC.balance}}{{this.communityFunds.balanceWBTC.balanceUsd}} USD
ETH{{this.communityFunds.balanceETH.balance}}{{this.communityFunds.balanceETH.balanceUsd}} USD
\ No newline at end of file diff --git a/app/routes/application.js b/app/routes/application.js index 638ddd3..a7470a0 100644 --- a/app/routes/application.js +++ b/app/routes/application.js @@ -3,8 +3,8 @@ import { inject as service } from '@ember/service'; import { schedule } from '@ember/runloop'; export default class ApplicationRoute extends Route { - @service kredits; + @service communityFunds; beforeModel(/* transition */) { const kredits = this.kredits; @@ -30,6 +30,8 @@ export default class ApplicationRoute extends Route { schedule('afterRender', this.kredits.syncContributors, this.kredits.syncContributors.perform); } - } + schedule('afterRender', this.communityFunds.fetchBalances, + this.communityFunds.fetchBalances.perform); + } } diff --git a/app/services/community-funds.js b/app/services/community-funds.js new file mode 100644 index 0000000..8c6eabd --- /dev/null +++ b/app/services/community-funds.js @@ -0,0 +1,32 @@ +import Service from '@ember/service'; +import { tracked } from '@glimmer/tracking'; +import { task } from 'ember-concurrency-decorators'; +import config from 'kredits-web/config/environment'; + +const txServiceBaseUrl = `${config.gnosisSafe.txServiceHost}/api/v1/safes/${config.gnosisSafe.address}`; + +export default class CommunityFundsService extends Service { + @tracked balanceETH = { balance: 0, balanceUsd: 0, usdConversion: 0 }; + @tracked balanceWBTC = { balance: 0, balanceUsd: 0, usdConversion: 0 }; + + @task + *fetchBalances () { + const uri = `${txServiceBaseUrl}/balances/usd/`; + + yield fetch(uri).then(res => res.json()) + .then(res => { + this.processBalances(res); + }) + .catch(err => { + console.log(`[community-funds] Fetching balances failed:`); + console.error(err); + }); + } + + processBalances (res) { + // TODO proper selection/find + const { balance, balanceUsd, usdConversion } = res[0]; + this.balanceETH = { balance, balanceUsd, usdConversion }; + this.balanceWBTC = res[1]; + } +} diff --git a/app/services/kredits.js b/app/services/kredits.js index 0e1bcff..391b8d3 100644 --- a/app/services/kredits.js +++ b/app/services/kredits.js @@ -21,7 +21,7 @@ import Contributor from 'kredits-web/models/contributor'; import Contribution from 'kredits-web/models/contribution'; import Reimbursement from 'kredits-web/models/reimbursement'; // Lets us access the model classes dynamically -const models = { Contributor, Contribution, Reimbursement } +const models = { Contributor, Contribution, Reimbursement }; export default Service.extend({ diff --git a/app/templates/budget.hbs b/app/templates/budget.hbs index 066cd82..fcac21c 100644 --- a/app/templates/budget.hbs +++ b/app/templates/budget.hbs @@ -22,27 +22,7 @@

Community funds

- - - - - - - - - - - - - - - - - - - - -
TokenAmountFiat value
WBTC1.1721511031.15 USD
ETH0.5115.60 USD
+
diff --git a/config/environment.js b/config/environment.js index e2c59ea..9d18af7 100644 --- a/config/environment.js +++ b/config/environment.js @@ -50,6 +50,11 @@ module.exports = function(environment) { tokens: { 'WBTC': '0x2260fac5e5542a773aa44fbcfedf7c193bc2c599' + }, + + gnosisSafe: { + txServiceHost: 'https://safe-transaction.mainnet.gnosis.io', + address: '0x9CC29b8373FF92B01C1f09F31B5DD862350c167E' } }; diff --git a/package-lock.json b/package-lock.json index 4669c89..fdfe8d9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2582,6 +2582,15 @@ "integrity": "sha512-tGRdvgC9/QMQSuSuJV45xoyhI0Pzjm7A9o/MVVA3HakXIImJbbzx/k/6dO9CUEQXIyS2y0fW6C1XaYOG7rY0FQ==", "dev": true }, + "@ember-decorators/utils": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/@ember-decorators/utils/-/utils-6.1.1.tgz", + "integrity": "sha512-0KqnoeoLKb6AyoSU65TRF5T85wmS4uDn06oARddwNPxxf/lt5jQlh41uX3W7V/fWL9tPu8x1L1Vvpc80MN1+YA==", + "dev": true, + "requires": { + "ember-cli-babel": "^7.1.3" + } + }, "@ember/edition-utils": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/@ember/edition-utils/-/edition-utils-1.2.0.tgz", @@ -11284,6 +11293,263 @@ "ember-maybe-import-regenerator": "^0.1.6" } }, + "ember-concurrency-decorators": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/ember-concurrency-decorators/-/ember-concurrency-decorators-2.0.1.tgz", + "integrity": "sha512-KJ8mCZxmvgalawh/zytURsc5ParNr9avPeCDR/mE7VQxWvITHtyAuYS2s1PPFQAQggTXTZYcGnfgzjn/cDHNMQ==", + "dev": true, + "requires": { + "@ember-decorators/utils": "^6.1.0", + "ember-cli-babel": "^7.19.0", + "ember-cli-htmlbars": "^4.3.1", + "ember-cli-typescript": "^3.1.4" + }, + "dependencies": { + "@babel/helper-plugin-utils": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.4.tgz", + "integrity": "sha512-O4KCvQA6lLiMU9l2eawBPMf1xPP8xPfB3iEQw150hOVTqj/rfXz0ThTb4HEzqQfs2Bmo5Ay8BzxfzVtBrr9dVg==", + "dev": true + }, + "@babel/plugin-transform-typescript": { + "version": "7.8.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.8.7.tgz", + "integrity": "sha512-7O0UsPQVNKqpHeHLpfvOG4uXmlw+MOxYvUv6Otc9uH5SYMIxvF6eBdjkWvC3f9G+VXe0RsNExyAQBeTRug/wqQ==", + "dev": true, + "requires": { + "@babel/helper-create-class-features-plugin": "^7.8.3", + "@babel/helper-plugin-utils": "^7.8.3", + "@babel/plugin-syntax-typescript": "^7.8.3" + } + }, + "babel-plugin-htmlbars-inline-precompile": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/babel-plugin-htmlbars-inline-precompile/-/babel-plugin-htmlbars-inline-precompile-3.2.0.tgz", + "integrity": "sha512-IUeZmgs9tMUGXYu1vfke5I18yYJFldFGdNFQOWslXTnDWXzpwPih7QFduUqvT+awDpDuNtXpdt5JAf43Q1Hhzg==", + "dev": true + }, + "broccoli-persistent-filter": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/broccoli-persistent-filter/-/broccoli-persistent-filter-2.3.1.tgz", + "integrity": "sha512-hVsmIgCDrl2NFM+3Gs4Cr2TA6UPaIZip99hN8mtkaUPgM8UeVnCbxelCvBjUBHo0oaaqP5jzqqnRVvb568Yu5g==", + "dev": true, + "requires": { + "async-disk-cache": "^1.2.1", + "async-promise-queue": "^1.0.3", + "broccoli-plugin": "^1.0.0", + "fs-tree-diff": "^2.0.0", + "hash-for-dep": "^1.5.0", + "heimdalljs": "^0.2.1", + "heimdalljs-logger": "^0.1.7", + "mkdirp": "^0.5.1", + "promise-map-series": "^0.2.1", + "rimraf": "^2.6.1", + "rsvp": "^4.7.0", + "symlink-or-copy": "^1.0.1", + "sync-disk-cache": "^1.3.3", + "walk-sync": "^1.0.0" + }, + "dependencies": { + "broccoli-plugin": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/broccoli-plugin/-/broccoli-plugin-1.3.1.tgz", + "integrity": "sha512-DW8XASZkmorp+q7J4EeDEZz+LoyKLAd2XZULXyD9l4m9/hAKV3vjHmB1kiUshcWAYMgTP1m2i4NnqCE/23h6AQ==", + "dev": true, + "requires": { + "promise-map-series": "^0.2.1", + "quick-temp": "^0.1.3", + "rimraf": "^2.3.4", + "symlink-or-copy": "^1.1.8" + } + }, + "walk-sync": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/walk-sync/-/walk-sync-1.1.4.tgz", + "integrity": "sha512-nowc9thB/Jg0KW4TgxoRjLLYRPvl3DB/98S89r4ZcJqq2B0alNcKDh6pzLkBSkPMzRSMsJghJHQi79qw0YWEkA==", + "dev": true, + "requires": { + "@types/minimatch": "^3.0.3", + "ensure-posix-path": "^1.1.0", + "matcher-collection": "^1.1.1" + } + } + } + }, + "broccoli-plugin": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/broccoli-plugin/-/broccoli-plugin-3.1.0.tgz", + "integrity": "sha512-7w7FP8WJYjLvb0eaw27LO678TGGaom++49O1VYIuzjhXjK5kn2+AMlDm7CaUFw4F7CLGoVQeZ84d8gICMJa4lA==", + "dev": true, + "requires": { + "broccoli-node-api": "^1.6.0", + "broccoli-output-wrapper": "^2.0.0", + "fs-merger": "^3.0.1", + "promise-map-series": "^0.2.1", + "quick-temp": "^0.1.3", + "rimraf": "^2.3.4", + "symlink-or-copy": "^1.1.8" + } + }, + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "ember-cli-htmlbars": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/ember-cli-htmlbars/-/ember-cli-htmlbars-4.4.0.tgz", + "integrity": "sha512-ohgctqk7dXIZR4TgN0xRoUYltWhghFJgqmtuswQTpZ7p74RxI9PKx+E8WV/95mGcPzraesvMNBg5utQNvcqgNg==", + "dev": true, + "requires": { + "@ember/edition-utils": "^1.2.0", + "babel-plugin-htmlbars-inline-precompile": "^3.2.0", + "broccoli-debug": "^0.6.5", + "broccoli-persistent-filter": "^2.3.1", + "broccoli-plugin": "^3.1.0", + "common-tags": "^1.8.0", + "ember-cli-babel-plugin-helpers": "^1.1.0", + "fs-tree-diff": "^2.0.1", + "hash-for-dep": "^1.5.1", + "heimdalljs-logger": "^0.1.10", + "json-stable-stringify": "^1.0.1", + "semver": "^6.3.0", + "strip-bom": "^4.0.0", + "walk-sync": "^2.0.2" + } + }, + "ember-cli-typescript": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/ember-cli-typescript/-/ember-cli-typescript-3.1.4.tgz", + "integrity": "sha512-HJ73kL45OGRmIkPhBNFt31I1SGUvdZND+LCH21+qpq3pPlFpJG8GORyXpP+2ze8PbnITNLzwe5AwUrpyuRswdQ==", + "dev": true, + "requires": { + "@babel/plugin-proposal-nullish-coalescing-operator": "^7.4.4", + "@babel/plugin-proposal-optional-chaining": "^7.6.0", + "@babel/plugin-transform-typescript": "~7.8.0", + "ansi-to-html": "^0.6.6", + "broccoli-stew": "^3.0.0", + "debug": "^4.0.0", + "ember-cli-babel-plugin-helpers": "^1.0.0", + "execa": "^3.0.0", + "fs-extra": "^8.0.0", + "resolve": "^1.5.0", + "rsvp": "^4.8.1", + "semver": "^6.3.0", + "stagehand": "^1.0.0", + "walk-sync": "^2.0.0" + } + }, + "execa": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-3.4.0.tgz", + "integrity": "sha512-r9vdGQk4bmCuK1yKQu1KTwcT2zwfWdbdaXfCtAh+5nU/4fSX+JAb7vZGvI5naJrQlvONrEB20jeruESI69530g==", + "dev": true, + "requires": { + "cross-spawn": "^7.0.0", + "get-stream": "^5.0.0", + "human-signals": "^1.1.1", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.0", + "onetime": "^5.1.0", + "p-finally": "^2.0.0", + "signal-exit": "^3.0.2", + "strip-final-newline": "^2.0.0" + } + }, + "fs-extra": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "dev": true, + "requires": { + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + } + }, + "fs-tree-diff": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/fs-tree-diff/-/fs-tree-diff-2.0.1.tgz", + "integrity": "sha512-x+CfAZ/lJHQqwlD64pYM5QxWjzWhSjroaVsr8PW831zOApL55qPibed0c+xebaLWVr2BnHFoHdrwOv8pzt8R5A==", + "dev": true, + "requires": { + "@types/symlink-or-copy": "^1.2.0", + "heimdalljs-logger": "^0.1.7", + "object-assign": "^4.1.0", + "path-posix": "^1.0.0", + "symlink-or-copy": "^1.1.8" + } + }, + "graceful-fs": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", + "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", + "dev": true + }, + "is-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", + "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==", + "dev": true + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "requires": { + "path-key": "^3.0.0" + } + }, + "rsvp": { + "version": "4.8.5", + "resolved": "https://registry.npmjs.org/rsvp/-/rsvp-4.8.5.tgz", + "integrity": "sha512-nfMOlASu9OnRJo1mbEk2cz0D56a1MBNrJ7orjRZQG10XDyuvwksKbuXNp6qa+kbn839HwjwhBzhFmdsaEAfauA==", + "dev": true + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + }, + "walk-sync": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/walk-sync/-/walk-sync-2.2.0.tgz", + "integrity": "sha512-IC8sL7aB4/ZgFcGI2T1LczZeFWZ06b3zoHH7jBPyHxOtIIz1jppWHjjEXkOFvFojBVAK9pV7g47xOZ4LW3QLfg==", + "dev": true, + "requires": { + "@types/minimatch": "^3.0.3", + "ensure-posix-path": "^1.1.0", + "matcher-collection": "^2.0.0", + "minimatch": "^3.0.4" + }, + "dependencies": { + "matcher-collection": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/matcher-collection/-/matcher-collection-2.0.1.tgz", + "integrity": "sha512-daE62nS2ZQsDg9raM0IlZzLmI2u+7ZapXBwdoeBUKAYERPDDIc0qNqA8E0Rp2D+gspKR7BgIFP52GeujaGXWeQ==", + "dev": true, + "requires": { + "@types/minimatch": "^3.0.3", + "minimatch": "^3.0.2" + } + } + } + } + } + }, "ember-diff-attrs": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/ember-diff-attrs/-/ember-diff-attrs-0.2.2.tgz", diff --git a/package.json b/package.json index dc7b99c..387ebff 100644 --- a/package.json +++ b/package.json @@ -49,6 +49,7 @@ "ember-cli-sri": "^2.1.1", "ember-cli-uglify": "^3.0.0", "ember-concurrency": "^1.1.7", + "ember-concurrency-decorators": "^2.0.1", "ember-export-application-global": "^2.0.1", "ember-fetch": "^8.0.1", "ember-flatpickr": "^2.16.2", diff --git a/tests/integration/components/budget-balances/component-test.js b/tests/integration/components/budget-balances/component-test.js new file mode 100644 index 0000000..cdf9bdd --- /dev/null +++ b/tests/integration/components/budget-balances/component-test.js @@ -0,0 +1,26 @@ +import { module, test } from 'qunit'; +import { setupRenderingTest } from 'ember-qunit'; +import { render } from '@ember/test-helpers'; +import { hbs } from 'ember-cli-htmlbars'; + +module('Integration | Component | budget-balances', function(hooks) { + setupRenderingTest(hooks); + + test('it renders', async function(assert) { + // Set any properties with this.set('myProperty', 'value'); + // Handle any actions with this.set('myAction', function(val) { ... }); + + await render(hbs``); + + assert.equal(this.element.textContent.trim(), ''); + + // Template block usage: + await render(hbs` + + template block text + + `); + + assert.equal(this.element.textContent.trim(), 'template block text'); + }); +}); diff --git a/tests/unit/services/community-funds-test.js b/tests/unit/services/community-funds-test.js new file mode 100644 index 0000000..8c6fad6 --- /dev/null +++ b/tests/unit/services/community-funds-test.js @@ -0,0 +1,12 @@ +import { module, test } from 'qunit'; +import { setupTest } from 'ember-qunit'; + +module('Unit | Service | community-funds', function(hooks) { + setupTest(hooks); + + // Replace this with your real tests. + test('it exists', function(assert) { + let service = this.owner.lookup('service:community-funds'); + assert.ok(service); + }); +});