diff --git a/LICENSE b/LICENSE.md
similarity index 100%
rename from LICENSE
rename to LICENSE.md
diff --git a/README.md b/README.md
index 0495c16..9b2b039 100644
--- a/README.md
+++ b/README.md
@@ -4,13 +4,10 @@
## Usage
-1. Start [ganache](https://trufflesuite.com/ganache/) instance by running `npm start` in [ganache](./packages/ganache) folder
-2. Deploy [TestERC20.sol](./packages/client/src/contract/TestERC20.sol), [PaymentGatewayContract.sol](./packages/client/src/contract/PaymentGatewayContract.sol) by using [Remix IDE](https://remix-project.org/)
-3. Mint some tokens in `TestERC20` by calling `mint` method. The UI is using `18 digits` representation of token decimals, so you should pass an argument like `200000000000000000000`.
-4. Allow withdrawal in `TestERC20` by calling `approve` method (with `PaymentGatewayContract` **deployer address**). Pass **the same amount from the step 3**
-5. Update `PaymentGatewayContract` address in [params.ts](./packages/client/src/config/params.ts)
-6. Run this DApp by running `npm start` in [client](./packages/client) folder. Send some tokens
-7. Check admin menu by typing secret cheat-code `133337`
+1. Run `start:ganache` in separated terminal
+2. Run `deploy:contracts` to deploy contracts to ganache
+3. Run this DApp by running `npm start` in [client](./packages/client) folder. Send some tokens
+4. Check admin menu by typing secret cheat-code `133337`
## Contract source code
@@ -20,7 +17,7 @@
contract PaymentGatewayContract {
IERC20 private erc20;
- address owner;
+ address public owner;
uint256 public deployBlock;
diff --git a/packages/ganache/package-lock.json b/package-lock.json
similarity index 99%
rename from packages/ganache/package-lock.json
rename to package-lock.json
index 39f3cbc..64753f6 100644
--- a/packages/ganache/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,13 @@
{
- "name": "exchangepro",
+ "name": "erc20-payment-gateway",
"version": "1.0.0",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
- "name": "exchangepro",
+ "name": "erc20-payment-gateway",
"version": "1.0.0",
+ "license": "ISC",
"dependencies": {
"ganache-cli": "6.12.2",
"glob": "8.0.3",
@@ -1018,9 +1019,9 @@
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
},
"node_modules/minimatch": {
- "version": "5.1.2",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.2.tgz",
- "integrity": "sha512-bNH9mmM9qsJ2X4r2Nat1B//1dJVcn3+iBLa3IgqJ7EbGaDNepL9QSHOxN4ng33s52VMMhhIfgCYDk3C4ZmlDAg==",
+ "version": "5.1.6",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz",
+ "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==",
"dependencies": {
"brace-expansion": "^2.0.1"
},
@@ -1828,9 +1829,9 @@
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
},
"minimatch": {
- "version": "5.1.2",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.2.tgz",
- "integrity": "sha512-bNH9mmM9qsJ2X4r2Nat1B//1dJVcn3+iBLa3IgqJ7EbGaDNepL9QSHOxN4ng33s52VMMhhIfgCYDk3C4ZmlDAg==",
+ "version": "5.1.6",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz",
+ "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==",
"requires": {
"brace-expansion": "^2.0.1"
}
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..8cbc4c7
--- /dev/null
+++ b/package.json
@@ -0,0 +1,26 @@
+{
+ "name": "erc20-payment-gateway",
+ "version": "1.0.0",
+ "description": "",
+ "main": "index.js",
+ "scripts": {
+ "start:ganache": "node ./scripts/run-ganache.js",
+ "deploy:contracts": "cd ./packages/truffle && npm start"
+ },
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/react-declarative/erc20-payment-gateway.git"
+ },
+ "keywords": [],
+ "author": "",
+ "license": "ISC",
+ "bugs": {
+ "url": "https://github.com/react-declarative/erc20-payment-gateway/issues"
+ },
+ "homepage": "https://github.com/react-declarative/erc20-payment-gateway#readme",
+ "dependencies": {
+ "ganache-cli": "6.12.2",
+ "glob": "8.0.3",
+ "rimraf": "3.0.2"
+ }
+}
diff --git a/packages/client/package-lock.json b/packages/client/package-lock.json
index ab6b92d..b3a7da9 100644
--- a/packages/client/package-lock.json
+++ b/packages/client/package-lock.json
@@ -22,7 +22,7 @@
"notistack": "^2.0.5",
"path-to-regexp": "^6.2.0",
"react": "^18.2.0",
- "react-declarative": "^2.2.17",
+ "react-declarative": "2.2.112",
"react-dom": "^18.2.0",
"react-window": "^1.8.7",
"resize-observer-polyfill": "^1.5.1",
@@ -5641,6 +5641,15 @@
"node": ">=8.9"
}
},
+ "node_modules/adler-32": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/adler-32/-/adler-32-1.3.1.tgz",
+ "integrity": "sha512-ynZ4w/nUUv5rrsR8UUGoe1VC9hZj6V5hU9Qw1HlMDJGEJw5S7TfTErWTjMys6M7vr0YWcPqs3qAr4ss0nDfP+A==",
+ "peer": true,
+ "engines": {
+ "node": ">=0.8"
+ }
+ },
"node_modules/aes-js": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/aes-js/-/aes-js-3.0.0.tgz",
@@ -6617,6 +6626,19 @@
"node": ">=4"
}
},
+ "node_modules/cfb": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/cfb/-/cfb-1.2.2.tgz",
+ "integrity": "sha512-KfdUZsSOw19/ObEWasvBP/Ac4reZvAGauZhs6S/gqNhXhI7cKwvlH7ulj+dOEYnca4bm4SGo8C1bTAQvnTjgQA==",
+ "peer": true,
+ "dependencies": {
+ "adler-32": "~1.3.0",
+ "crc-32": "~1.2.0"
+ },
+ "engines": {
+ "node": ">=0.8"
+ }
+ },
"node_modules/chalk": {
"version": "2.4.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
@@ -6786,6 +6808,15 @@
"node": ">= 4.0"
}
},
+ "node_modules/codepage": {
+ "version": "1.15.0",
+ "resolved": "https://registry.npmjs.org/codepage/-/codepage-1.15.0.tgz",
+ "integrity": "sha512-3g6NUTPd/YtuuGrhMnOMRjFc+LJw/bnMp3+0r/Wcz3IXUuCosKRJvMphm5+Q+bvTVGcJJuRvVLuYba+WojaFaA==",
+ "peer": true,
+ "engines": {
+ "node": ">=0.8"
+ }
+ },
"node_modules/collect-v8-coverage": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz",
@@ -7027,6 +7058,18 @@
"node": ">=10"
}
},
+ "node_modules/crc-32": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz",
+ "integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==",
+ "peer": true,
+ "bin": {
+ "crc32": "bin/crc32.njs"
+ },
+ "engines": {
+ "node": ">=0.8"
+ }
+ },
"node_modules/cross-spawn": {
"version": "7.0.3",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
@@ -9598,6 +9641,15 @@
"node": ">= 0.6"
}
},
+ "node_modules/frac": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/frac/-/frac-1.1.2.tgz",
+ "integrity": "sha512-w/XBfkibaTl3YDqASwfDUqkna4Z2p9cFSr1aHDt0WoMTECnRfBOv2WArlZILlqgWlmdIlALXGpM2AOhEk5W3IA==",
+ "peer": true,
+ "engines": {
+ "node": ">=0.8"
+ }
+ },
"node_modules/fraction.js": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.2.0.tgz",
@@ -16022,9 +16074,9 @@
}
},
"node_modules/react-declarative": {
- "version": "2.2.17",
- "resolved": "https://registry.npmjs.org/react-declarative/-/react-declarative-2.2.17.tgz",
- "integrity": "sha512-rIivbYnCYEwwanitaAWng7Nc3vKDOvX/x7WVNPJ91dlT0DiLrNu/63a1brnJqij55OLwwDlHtwp/ALaEDK3s5w==",
+ "version": "2.2.112",
+ "resolved": "https://registry.npmjs.org/react-declarative/-/react-declarative-2.2.112.tgz",
+ "integrity": "sha512-ahBOX9YnMBiiLg/1wvqDslNzctB+q3fqZid07MiNC25LC/h2ffoNDAxPelRsHDEDhubQGe/vcJfdEJ/G6gDV5Q==",
"hasInstallScript": true,
"dependencies": {
"dayjs": "1.11.5",
@@ -16048,8 +16100,8 @@
"@mui/system": ">= 5.5.0 <= 5.11.0",
"react": "^17.0.0 || ^18.0.0",
"react-dom": "^17.0.0 || ^18.0.0",
- "react-window": "1.8.7",
- "tss-react": "4.3.4"
+ "tss-react": "4.3.4",
+ "xlsx": ">=0.18.5"
}
},
"node_modules/react-dev-utils": {
@@ -17292,6 +17344,18 @@
"integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==",
"dev": true
},
+ "node_modules/ssf": {
+ "version": "0.11.2",
+ "resolved": "https://registry.npmjs.org/ssf/-/ssf-0.11.2.tgz",
+ "integrity": "sha512-+idbmIXoYET47hH+d7dfm2epdOMUDjqcB4648sTZ+t2JwoyBFL/insLfB/racrDmsKB3diwsDA696pZMieAC5g==",
+ "peer": true,
+ "dependencies": {
+ "frac": "~1.1.2"
+ },
+ "engines": {
+ "node": ">=0.8"
+ }
+ },
"node_modules/stable": {
"version": "0.1.8",
"resolved": "https://registry.npmjs.org/stable/-/stable-0.1.8.tgz",
@@ -18858,6 +18922,24 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/wmf": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/wmf/-/wmf-1.0.2.tgz",
+ "integrity": "sha512-/p9K7bEh0Dj6WbXg4JG0xvLQmIadrner1bi45VMJTfnbVHsc7yIajZyoSoK60/dtVBs12Fm6WkUI5/3WAVsNMw==",
+ "peer": true,
+ "engines": {
+ "node": ">=0.8"
+ }
+ },
+ "node_modules/word": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/word/-/word-0.3.0.tgz",
+ "integrity": "sha512-OELeY0Q61OXpdUfTp+oweA/vtLVg5VDOXh+3he3PNzLGG/y0oylSOC1xRVj0+l4vQ3tj/bB1HVHv1ocXkQceFA==",
+ "peer": true,
+ "engines": {
+ "node": ">=0.8"
+ }
+ },
"node_modules/word-wrap": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz",
@@ -19275,6 +19357,27 @@
}
}
},
+ "node_modules/xlsx": {
+ "version": "0.18.5",
+ "resolved": "https://registry.npmjs.org/xlsx/-/xlsx-0.18.5.tgz",
+ "integrity": "sha512-dmg3LCjBPHZnQp5/F/+nnTa+miPJxUXB6vtk42YjBBKayDNagxGEeIdWApkYPOf3Z3pm3k62Knjzp7lMeTEtFQ==",
+ "peer": true,
+ "dependencies": {
+ "adler-32": "~1.3.0",
+ "cfb": "~1.2.1",
+ "codepage": "~1.15.0",
+ "crc-32": "~1.2.1",
+ "ssf": "~0.11.2",
+ "wmf": "~1.0.1",
+ "word": "~0.3.0"
+ },
+ "bin": {
+ "xlsx": "bin/xlsx.njs"
+ },
+ "engines": {
+ "node": ">=0.8"
+ }
+ },
"node_modules/xml-name-validator": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz",
@@ -23197,6 +23300,12 @@
"regex-parser": "^2.2.11"
}
},
+ "adler-32": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/adler-32/-/adler-32-1.3.1.tgz",
+ "integrity": "sha512-ynZ4w/nUUv5rrsR8UUGoe1VC9hZj6V5hU9Qw1HlMDJGEJw5S7TfTErWTjMys6M7vr0YWcPqs3qAr4ss0nDfP+A==",
+ "peer": true
+ },
"aes-js": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/aes-js/-/aes-js-3.0.0.tgz",
@@ -23932,6 +24041,16 @@
"integrity": "sha512-roIFONhcxog0JSSWbvVAh3OocukmSgpqOH6YpMkCvav/ySIV3JKg4Dc8vYtQjYi/UxpNE36r/9v+VqTQqgkYmw==",
"dev": true
},
+ "cfb": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/cfb/-/cfb-1.2.2.tgz",
+ "integrity": "sha512-KfdUZsSOw19/ObEWasvBP/Ac4reZvAGauZhs6S/gqNhXhI7cKwvlH7ulj+dOEYnca4bm4SGo8C1bTAQvnTjgQA==",
+ "peer": true,
+ "requires": {
+ "adler-32": "~1.3.0",
+ "crc-32": "~1.2.0"
+ }
+ },
"chalk": {
"version": "2.4.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
@@ -24056,6 +24175,12 @@
"q": "^1.1.2"
}
},
+ "codepage": {
+ "version": "1.15.0",
+ "resolved": "https://registry.npmjs.org/codepage/-/codepage-1.15.0.tgz",
+ "integrity": "sha512-3g6NUTPd/YtuuGrhMnOMRjFc+LJw/bnMp3+0r/Wcz3IXUuCosKRJvMphm5+Q+bvTVGcJJuRvVLuYba+WojaFaA==",
+ "peer": true
+ },
"collect-v8-coverage": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz",
@@ -24255,6 +24380,12 @@
"yaml": "^1.10.0"
}
},
+ "crc-32": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz",
+ "integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==",
+ "peer": true
+ },
"cross-spawn": {
"version": "7.0.3",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
@@ -26179,6 +26310,12 @@
"integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==",
"dev": true
},
+ "frac": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/frac/-/frac-1.1.2.tgz",
+ "integrity": "sha512-w/XBfkibaTl3YDqASwfDUqkna4Z2p9cFSr1aHDt0WoMTECnRfBOv2WArlZILlqgWlmdIlALXGpM2AOhEk5W3IA==",
+ "peer": true
+ },
"fraction.js": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.2.0.tgz",
@@ -30734,9 +30871,9 @@
}
},
"react-declarative": {
- "version": "2.2.17",
- "resolved": "https://registry.npmjs.org/react-declarative/-/react-declarative-2.2.17.tgz",
- "integrity": "sha512-rIivbYnCYEwwanitaAWng7Nc3vKDOvX/x7WVNPJ91dlT0DiLrNu/63a1brnJqij55OLwwDlHtwp/ALaEDK3s5w==",
+ "version": "2.2.112",
+ "resolved": "https://registry.npmjs.org/react-declarative/-/react-declarative-2.2.112.tgz",
+ "integrity": "sha512-ahBOX9YnMBiiLg/1wvqDslNzctB+q3fqZid07MiNC25LC/h2ffoNDAxPelRsHDEDhubQGe/vcJfdEJ/G6gDV5Q==",
"requires": {
"dayjs": "1.11.5",
"history": "5.3.0",
@@ -31686,6 +31823,15 @@
"integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==",
"dev": true
},
+ "ssf": {
+ "version": "0.11.2",
+ "resolved": "https://registry.npmjs.org/ssf/-/ssf-0.11.2.tgz",
+ "integrity": "sha512-+idbmIXoYET47hH+d7dfm2epdOMUDjqcB4648sTZ+t2JwoyBFL/insLfB/racrDmsKB3diwsDA696pZMieAC5g==",
+ "peer": true,
+ "requires": {
+ "frac": "~1.1.2"
+ }
+ },
"stable": {
"version": "0.1.8",
"resolved": "https://registry.npmjs.org/stable/-/stable-0.1.8.tgz",
@@ -32860,6 +33006,18 @@
"is-typed-array": "^1.1.10"
}
},
+ "wmf": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/wmf/-/wmf-1.0.2.tgz",
+ "integrity": "sha512-/p9K7bEh0Dj6WbXg4JG0xvLQmIadrner1bi45VMJTfnbVHsc7yIajZyoSoK60/dtVBs12Fm6WkUI5/3WAVsNMw==",
+ "peer": true
+ },
+ "word": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/word/-/word-0.3.0.tgz",
+ "integrity": "sha512-OELeY0Q61OXpdUfTp+oweA/vtLVg5VDOXh+3he3PNzLGG/y0oylSOC1xRVj0+l4vQ3tj/bB1HVHv1ocXkQceFA==",
+ "peer": true
+ },
"word-wrap": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz",
@@ -33223,6 +33381,21 @@
"integrity": "sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A==",
"requires": {}
},
+ "xlsx": {
+ "version": "0.18.5",
+ "resolved": "https://registry.npmjs.org/xlsx/-/xlsx-0.18.5.tgz",
+ "integrity": "sha512-dmg3LCjBPHZnQp5/F/+nnTa+miPJxUXB6vtk42YjBBKayDNagxGEeIdWApkYPOf3Z3pm3k62Knjzp7lMeTEtFQ==",
+ "peer": true,
+ "requires": {
+ "adler-32": "~1.3.0",
+ "cfb": "~1.2.1",
+ "codepage": "~1.15.0",
+ "crc-32": "~1.2.1",
+ "ssf": "~0.11.2",
+ "wmf": "~1.0.1",
+ "word": "~0.3.0"
+ }
+ },
"xml-name-validator": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz",
diff --git a/packages/client/package.json b/packages/client/package.json
index 2c67604..562a4da 100644
--- a/packages/client/package.json
+++ b/packages/client/package.json
@@ -17,7 +17,7 @@
"notistack": "^2.0.5",
"path-to-regexp": "^6.2.0",
"react": "^18.2.0",
- "react-declarative": "^2.2.17",
+ "react-declarative": "2.2.112",
"react-dom": "^18.2.0",
"react-window": "^1.8.7",
"resize-observer-polyfill": "^1.5.1",
diff --git a/packages/client/public/avatar.png b/packages/client/public/avatar.png
new file mode 100644
index 0000000..9f86284
Binary files /dev/null and b/packages/client/public/avatar.png differ
diff --git a/packages/client/public/index.html b/packages/client/public/index.html
index 5534c50..4113cbd 100644
--- a/packages/client/public/index.html
+++ b/packages/client/public/index.html
@@ -13,6 +13,7 @@
scrollbar-width: none;
}
+
diff --git a/packages/client/src/components/common/Logo.tsx b/packages/client/src/components/common/Logo.tsx
index 7e92816..f6ea191 100644
--- a/packages/client/src/components/common/Logo.tsx
+++ b/packages/client/src/components/common/Logo.tsx
@@ -1,21 +1,115 @@
+import { useMemo } from "react";
+import { Async, typo } from 'react-declarative';
+import { darken, Theme } from "@mui/material";
+import { makeStyles } from "../../styles/makeStyles";
+
+import CircularProgress from "@mui/material/CircularProgress";
import Typography from "@mui/material/Typography";
+import Avatar from "@mui/material/Avatar";
+import Stack from "@mui/material/Stack";
+import Link from "@mui/material/Link";
+import Box from "@mui/material/Box";
+
+import { observer } from "mobx-react-lite";
+
+import ioc from '../../lib/ioc';
-import { CC_APP_NAME } from "../../config/params";
+const LOGO_HEIGHT = 255;
-export const Logo = () => (
- ({
+ root: {
+ position: "absolute",
+ overflow: "hidden",
+ display: "flex",
+ alignItems: "center",
+ justifyContent: "center",
+ top: 0,
+ left: 0,
+ right: 0,
+ width: "100%",
+ height: LOGO_HEIGHT,
+ background: darken(theme.palette.background.paper, 0.12),
+ },
+ adjust: {
+ paddingBottom: LOGO_HEIGHT,
+ },
+}));
+
+const Loader = () => (
+
- {CC_APP_NAME}
-
+
+
);
+export const Logo = observer(() => {
+ const { classes } = useStyles();
+
+ const isEtherscanAvailable = useMemo(() => {
+ let isOk = true;
+
+ isOk = isOk && ioc.ethersService.isMetamaskAvailable;
+ isOk = isOk && ioc.ethersService.isProviderConnected;
+ isOk = isOk && ioc.ethersService.isAccountEnabled;
+
+ isOk = isOk && ioc.paymentGatewayService.isContractConnected;
+ isOk = isOk && ioc.erc20Service.isContractConnected;
+
+ return isOk;
+ }, []);
+
+ return (
+ <>
+
+
+
+ Peter Tripolsky
+ theme.palette.text.secondary }}
+ >
+
+ {async () => {
+ if (isEtherscanAvailable) {
+ const address = await ioc.paymentGatewayService.getOwner();
+ return (
+ <>
+ Direct etherscan address is
+
+
+ {address}
+
+ >
+ );
+ }
+ return "Dotation recipient";
+ }}
+
+
+
+
+
+ >
+ );
+});
+
export default Logo;
diff --git a/packages/client/src/config/params.ts b/packages/client/src/config/params.ts
index fdff7f7..672a88a 100644
--- a/packages/client/src/config/params.ts
+++ b/packages/client/src/config/params.ts
@@ -1,4 +1,18 @@
-export const CC_CONTRACT_ADDRESS = /*process.env.REACT_APP_CONTRACT ||*/ '0x671843106E07f9D835d7299381CD14863af18593';
-export const CC_APP_NAME = 'HashApp';
-export { default as CC_CONTRACT_ABI } from "../contract/ABI.json";
-export const CC_CONTRACT_DECIMALS = Math.pow(10, 18);
+import CC_PAYMENT_GATEWAY_ABI from "../contract/payment-gateway.abi.json";
+import CC_ERC20_ABI from "../contract/erc20.abi.json";
+
+import ADDRESSES from "../contract/instances.deployed.json";
+
+const CC_ERC20_ADDRESS = ADDRESSES.erc20;
+const CC_PAYMENT_GATEWAY_ADDRESS = ADDRESSES.gateway;
+const CC_LESSON_PRICE = 25;
+const CC_DEFAULT_QUANTITY = 1;
+
+export {
+ CC_ERC20_ADDRESS,
+ CC_PAYMENT_GATEWAY_ADDRESS,
+ CC_DEFAULT_QUANTITY,
+ CC_LESSON_PRICE,
+ CC_ERC20_ABI,
+ CC_PAYMENT_GATEWAY_ABI,
+};
diff --git a/packages/client/src/config/routes.tsx b/packages/client/src/config/routes.tsx
index 5a03619..cc1c27d 100644
--- a/packages/client/src/config/routes.tsx
+++ b/packages/client/src/config/routes.tsx
@@ -4,6 +4,7 @@ import ConnectPage from "../pages/ConnectPage";
import PermissionPage from "../pages/PermissionPage";
import NoMetamaskPage from "../pages/NoMetamaskPage";
import NotDeployedPage from "../pages/NotDeployedPage";
+import DonePage from "../pages/DonePage";
import ErrorPage from "../pages/ErrorPage";
import MainPage from "../pages/MainPage";
@@ -34,9 +35,15 @@ export const routes: ISwitchItem[] = [
{
path: "/main-page",
element: MainPage,
- prefetch: () => ioc.contractService.prefetch(),
+ prefetch: async () => await Promise.all([
+ ioc.erc20Service.prefetch(),
+ ioc.paymentGatewayService.prefetch(),
+ ]),
redirect: () => {
- if (!ioc.contractService.isContractConnected) {
+ if (!ioc.paymentGatewayService.isContractConnected) {
+ return "/notdeployed-page";
+ }
+ if (!ioc.erc20Service.isContractConnected) {
return "/notdeployed-page";
}
return null;
@@ -45,9 +52,15 @@ export const routes: ISwitchItem[] = [
{
path: "/admin-page",
element: AdminPage,
- prefetch: () => ioc.contractService.prefetch(),
+ prefetch: async () => await Promise.all([
+ ioc.erc20Service.prefetch(),
+ ioc.paymentGatewayService.prefetch(),
+ ]),
redirect: () => {
- if (!ioc.contractService.isContractConnected) {
+ if (!ioc.paymentGatewayService.isContractConnected) {
+ return "/notdeployed-page";
+ }
+ if (!ioc.erc20Service.isContractConnected) {
return "/notdeployed-page";
}
return null;
@@ -69,6 +82,10 @@ export const routes: ISwitchItem[] = [
path: "/error-page",
element: ErrorPage,
},
+ {
+ path: "/done-page",
+ element: DonePage,
+ },
];
export default routes;
diff --git a/packages/client/src/contract/erc20.abi.json b/packages/client/src/contract/erc20.abi.json
new file mode 100644
index 0000000..405d6b3
--- /dev/null
+++ b/packages/client/src/contract/erc20.abi.json
@@ -0,0 +1,222 @@
+[
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "name",
+ "outputs": [
+ {
+ "name": "",
+ "type": "string"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": false,
+ "inputs": [
+ {
+ "name": "_spender",
+ "type": "address"
+ },
+ {
+ "name": "_value",
+ "type": "uint256"
+ }
+ ],
+ "name": "approve",
+ "outputs": [
+ {
+ "name": "",
+ "type": "bool"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "totalSupply",
+ "outputs": [
+ {
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": false,
+ "inputs": [
+ {
+ "name": "_from",
+ "type": "address"
+ },
+ {
+ "name": "_to",
+ "type": "address"
+ },
+ {
+ "name": "_value",
+ "type": "uint256"
+ }
+ ],
+ "name": "transferFrom",
+ "outputs": [
+ {
+ "name": "",
+ "type": "bool"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "decimals",
+ "outputs": [
+ {
+ "name": "",
+ "type": "uint8"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [
+ {
+ "name": "_owner",
+ "type": "address"
+ }
+ ],
+ "name": "balanceOf",
+ "outputs": [
+ {
+ "name": "balance",
+ "type": "uint256"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "symbol",
+ "outputs": [
+ {
+ "name": "",
+ "type": "string"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": false,
+ "inputs": [
+ {
+ "name": "_to",
+ "type": "address"
+ },
+ {
+ "name": "_value",
+ "type": "uint256"
+ }
+ ],
+ "name": "transfer",
+ "outputs": [
+ {
+ "name": "",
+ "type": "bool"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [
+ {
+ "name": "_owner",
+ "type": "address"
+ },
+ {
+ "name": "_spender",
+ "type": "address"
+ }
+ ],
+ "name": "allowance",
+ "outputs": [
+ {
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "payable": true,
+ "stateMutability": "payable",
+ "type": "fallback"
+ },
+ {
+ "anonymous": false,
+ "inputs": [
+ {
+ "indexed": true,
+ "name": "owner",
+ "type": "address"
+ },
+ {
+ "indexed": true,
+ "name": "spender",
+ "type": "address"
+ },
+ {
+ "indexed": false,
+ "name": "value",
+ "type": "uint256"
+ }
+ ],
+ "name": "Approval",
+ "type": "event"
+ },
+ {
+ "anonymous": false,
+ "inputs": [
+ {
+ "indexed": true,
+ "name": "from",
+ "type": "address"
+ },
+ {
+ "indexed": true,
+ "name": "to",
+ "type": "address"
+ },
+ {
+ "indexed": false,
+ "name": "value",
+ "type": "uint256"
+ }
+ ],
+ "name": "Transfer",
+ "type": "event"
+ }
+]
diff --git a/packages/client/src/contract/instances.deployed.json b/packages/client/src/contract/instances.deployed.json
new file mode 100644
index 0000000..c4f10ca
--- /dev/null
+++ b/packages/client/src/contract/instances.deployed.json
@@ -0,0 +1,4 @@
+{
+ "gateway": "0x88f0d83ea7f33da416381A964B4742DBAB404551",
+ "erc20": "0x55D02Fe98CAA966E59e546d1f878746a19C95F72"
+}
\ No newline at end of file
diff --git a/packages/client/src/contract/ABI.json b/packages/client/src/contract/payment-gateway.abi.json
similarity index 84%
rename from packages/client/src/contract/ABI.json
rename to packages/client/src/contract/payment-gateway.abi.json
index ed693e1..1939967 100644
--- a/packages/client/src/contract/ABI.json
+++ b/packages/client/src/contract/payment-gateway.abi.json
@@ -48,6 +48,19 @@
"stateMutability": "view",
"type": "function"
},
+ {
+ "inputs": [],
+ "name": "owner",
+ "outputs": [
+ {
+ "internalType": "address",
+ "name": "",
+ "type": "address"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
{
"inputs": [
{
@@ -66,4 +79,4 @@
"stateMutability": "nonpayable",
"type": "function"
}
-]
+]
\ No newline at end of file
diff --git a/packages/client/src/lib/config.ts b/packages/client/src/lib/config.ts
index 30ece38..5dcef57 100644
--- a/packages/client/src/lib/config.ts
+++ b/packages/client/src/lib/config.ts
@@ -1,15 +1,18 @@
import { provide } from 'react-declarative';
-import EthersService from "./services/EthersService";
-import ContractService from "./services/ContractService";
-import RouterService from './services/RouterService';
-import AlertService from './services/AlertService';
-import LayoutService from './services/LayoutService';
-import ConnectService from './services/ConnectService';
+import EthersService from "./services/base/EthersService";
+import RouterService from './services/base/RouterService';
+import AlertService from './services/base/AlertService';
+import LayoutService from './services/base/LayoutService';
+import ConnectService from './services/base/ConnectService';
+
+import PaymentGatewayService from "./services/app/PaymentGatewayService";
+import Erc20Service from "./services/app/Erc20Service";
import TYPES from "./types";
-provide(TYPES.contractService, () => new ContractService());
+provide(TYPES.paymentGatewayService, () => new PaymentGatewayService());
+provide(TYPES.erc20Service, () => new Erc20Service());
provide(TYPES.ethersService, () => new EthersService());
provide(TYPES.routerService, () => new RouterService());
provide(TYPES.alertService, () => new AlertService());
diff --git a/packages/client/src/lib/ioc.ts b/packages/client/src/lib/ioc.ts
index b98973e..bfdb0de 100644
--- a/packages/client/src/lib/ioc.ts
+++ b/packages/client/src/lib/ioc.ts
@@ -1,18 +1,19 @@
import { inject } from 'react-declarative';
-import ContractService from "./services/ContractService";
-import EthersService from "./services/EthersService";
-import RouterService from './services/RouterService';
-import AlertService from './services/AlertService';
-import ConnectService from './services/ConnectService';
-import LayoutService from './services/LayoutService';
+import EthersService from "./services/base/EthersService";
+import RouterService from './services/base/RouterService';
+import AlertService from './services/base/AlertService';
+import ConnectService from './services/base/ConnectService';
+import LayoutService from './services/base/LayoutService';
+
+import PaymentGatewayService from "./services/app/PaymentGatewayService";
+import Erc20Service from "./services/app/Erc20Service";
import "./config"
import TYPES from "./types";
const baseServices = {
- contractService: inject(TYPES.contractService),
ethersService: inject(TYPES.ethersService),
routerService: inject(TYPES.routerService),
alertService: inject(TYPES.alertService),
@@ -20,8 +21,14 @@ const baseServices = {
layoutService: inject(TYPES.layoutService),
};
+const appServices = {
+ paymentGatewayService: inject(TYPES.paymentGatewayService),
+ erc20Service: inject(TYPES.erc20Service),
+};
+
export const ioc = {
...baseServices,
+ ...appServices,
};
window.addEventListener('unhandledrejection', () => {
@@ -32,8 +39,6 @@ window.addEventListener('unhandledrejection', () => {
ioc.routerService.push('/error-page');
});*/
-// if (process.env.REACT_APP_STAGE === 'dev') {
- (window as any).ioc = ioc;
-// }
+(window as any).ioc = ioc;
export default ioc;
diff --git a/packages/client/src/lib/services/app/Erc20Service.ts b/packages/client/src/lib/services/app/Erc20Service.ts
new file mode 100644
index 0000000..4648312
--- /dev/null
+++ b/packages/client/src/lib/services/app/Erc20Service.ts
@@ -0,0 +1,68 @@
+import { makeAutoObservable, runInAction } from "mobx";
+import { inject, singleshot, toBytes32, fromBytes32 } from "react-declarative";
+
+import {
+ ethers,
+ BaseContract,
+ /*BigNumber,*/
+} from "ethers";
+
+import EthersService from "../base/EthersService";
+
+import { CC_ERC20_ABI } from "../../../config/params";
+import { CC_ERC20_ADDRESS } from "../../../config/params";
+
+import TYPES from "../../types";
+
+type IContract = BaseContract & Record Promise>;
+
+export class Erc20Service {
+
+ private readonly ethersService = inject(TYPES.ethersService);
+
+ private _instance: IContract = null as never;
+
+ get isContractConnected() {
+ return !!this._instance;
+ };
+
+ constructor() {
+ makeAutoObservable(this);
+ };
+
+ getSymbol = singleshot(async () => String(await this._instance.symbol()));
+
+ getDecimals = singleshot(async () => Number(await this._instance.decimals()));
+
+ balanceOf = async (address: string) => Number(await this._instance.balanceOf(address));
+
+ balanceOfOwner = async () => Number(await this._instance.balanceOf(await this.ethersService.getAccount()));
+
+ transfer = async (address: string, amount: number) => Number(await this._instance.transfer(address, String(amount)));
+
+ approve = async (address: string, amount: number) => {
+ const result = await this._instance.approve(address, String(amount));
+ await result.wait();
+ };
+
+ prefetch = singleshot(async () => {
+ console.log("Erc20Service prefetch started");
+ try {
+ const deployedCode = await this.ethersService.getCode(CC_ERC20_ADDRESS);
+ if (deployedCode === '0x') {
+ throw new Error('Erc20Service contract not deployed');
+ }
+ const instance = new ethers.Contract(
+ CC_ERC20_ADDRESS,
+ CC_ERC20_ABI,
+ this.ethersService.getSigner(),
+ ) as IContract;
+ runInAction(() => this._instance = instance);
+ } catch (e) {
+ console.warn('Erc20Service prefetch failed', e);
+ }
+ });
+
+};
+
+export default Erc20Service;
diff --git a/packages/client/src/lib/services/ContractService.ts b/packages/client/src/lib/services/app/PaymentGatewayService.ts
similarity index 75%
rename from packages/client/src/lib/services/ContractService.ts
rename to packages/client/src/lib/services/app/PaymentGatewayService.ts
index fb3efc4..e5ff42f 100644
--- a/packages/client/src/lib/services/ContractService.ts
+++ b/packages/client/src/lib/services/app/PaymentGatewayService.ts
@@ -7,16 +7,16 @@ import {
/*BigNumber,*/
} from "ethers";
-import EthersService from "./EthersService";
+import EthersService from "../base/EthersService";
-import { CC_CONTRACT_ADDRESS } from "../../config/params";
-import { CC_CONTRACT_ABI } from "../../config/params";
+import { CC_PAYMENT_GATEWAY_ABI } from "../../../config/params";
+import { CC_PAYMENT_GATEWAY_ADDRESS } from "../../../config/params";
-import TYPES from "../types";
+import TYPES from "../../types";
type IContract = BaseContract & Record Promise>;
-export class ContractService {
+export class PaymentGatewayService {
private readonly ethersService = inject(TYPES.ethersService);
@@ -36,7 +36,9 @@ export class ContractService {
makeAutoObservable(this);
};
- getDeployBlock = async () => Number(await this._instance.deployBlock());
+ getDeployBlock = singleshot(async () => Number(await this._instance.deployBlock()));
+
+ getOwner = singleshot(async () => await this._instance.owner());
sendUSDT = async (_amount: number, _data: string) => {
const result = await this._instance.sendUSDT(String(_amount), toBytes32(_data));
@@ -55,9 +57,9 @@ export class ContractService {
const eventTopic = ethers.utils.id(eventSignature);
const deployBlock = await this.getDeployBlock();
const currentBlock = await this.ethersService.provider.getBlockNumber();
- const parser = new ethers.utils.Interface(CC_CONTRACT_ABI);
+ const parser = new ethers.utils.Interface(CC_PAYMENT_GATEWAY_ABI);
const rawLogs = await this.ethersService.provider.getLogs({
- address: CC_CONTRACT_ADDRESS,
+ address: CC_PAYMENT_GATEWAY_ADDRESS,
topics: [eventTopic],
fromBlock: deployBlock,
toBlock: currentBlock,
@@ -75,15 +77,15 @@ export class ContractService {
};
prefetch = singleshot(async () => {
- console.log("ContractService prefetch started");
+ console.log("PaymentGatewayService prefetch started");
try {
- const deployedCode = await this.ethersService.getCode(CC_CONTRACT_ADDRESS);
+ const deployedCode = await this.ethersService.getCode(CC_PAYMENT_GATEWAY_ADDRESS);
if (deployedCode === '0x') {
- throw new Error('ContractService contract not deployed');
+ throw new Error('PaymentGatewayService contract not deployed');
}
const instance = new ethers.Contract(
- CC_CONTRACT_ADDRESS,
- CC_CONTRACT_ABI,
+ CC_PAYMENT_GATEWAY_ADDRESS,
+ CC_PAYMENT_GATEWAY_ABI,
this.ethersService.getSigner(),
) as IContract;
runInAction(() => this._instance = instance);
@@ -97,10 +99,10 @@ export class ContractService {
});
});
} catch (e) {
- console.warn('ContractService prefetch failed', e);
+ console.warn('PaymentGatewayService prefetch failed', e);
}
});
};
-export default ContractService;
+export default PaymentGatewayService;
diff --git a/packages/client/src/lib/services/AlertService.ts b/packages/client/src/lib/services/base/AlertService.ts
similarity index 100%
rename from packages/client/src/lib/services/AlertService.ts
rename to packages/client/src/lib/services/base/AlertService.ts
diff --git a/packages/client/src/lib/services/ConnectService.ts b/packages/client/src/lib/services/base/ConnectService.ts
similarity index 97%
rename from packages/client/src/lib/services/ConnectService.ts
rename to packages/client/src/lib/services/base/ConnectService.ts
index 53f1a67..5897094 100644
--- a/packages/client/src/lib/services/ConnectService.ts
+++ b/packages/client/src/lib/services/base/ConnectService.ts
@@ -5,7 +5,7 @@ import RouterService from "./RouterService";
import EthersService from "./EthersService";
import LayoutService from "./LayoutService";
-import TYPES from "../types";
+import TYPES from "../../types";
export class ConnectPageService {
diff --git a/packages/client/src/lib/services/EthersService.ts b/packages/client/src/lib/services/base/EthersService.ts
similarity index 100%
rename from packages/client/src/lib/services/EthersService.ts
rename to packages/client/src/lib/services/base/EthersService.ts
diff --git a/packages/client/src/lib/services/LayoutService.ts b/packages/client/src/lib/services/base/LayoutService.ts
similarity index 100%
rename from packages/client/src/lib/services/LayoutService.ts
rename to packages/client/src/lib/services/base/LayoutService.ts
diff --git a/packages/client/src/lib/services/RouterService.ts b/packages/client/src/lib/services/base/RouterService.ts
similarity index 100%
rename from packages/client/src/lib/services/RouterService.ts
rename to packages/client/src/lib/services/base/RouterService.ts
diff --git a/packages/client/src/lib/types.ts b/packages/client/src/lib/types.ts
index ea2eaf9..5552f7b 100644
--- a/packages/client/src/lib/types.ts
+++ b/packages/client/src/lib/types.ts
@@ -1,14 +1,19 @@
const baseServices = {
ethersService: Symbol.for('ethersService'),
- contractService: Symbol.for('contractService'),
routerService: Symbol.for('routerService'),
alertService: Symbol.for('alertService'),
layoutService: Symbol.for('layoutService'),
connectService: Symbol.for('connectService'),
};
+const appServices = {
+ paymentGatewayService: Symbol.for('paymentGatewayService'),
+ erc20Service: Symbol.for('erc20Service'),
+};
+
export const TYPES = {
...baseServices,
+ ...appServices,
};
export default TYPES;
\ No newline at end of file
diff --git a/packages/client/src/pages/AdminPage.tsx b/packages/client/src/pages/AdminPage.tsx
index 4adf8db..5bef47c 100644
--- a/packages/client/src/pages/AdminPage.tsx
+++ b/packages/client/src/pages/AdminPage.tsx
@@ -59,7 +59,7 @@ const heightRequest = () => {
};
export const AdminPage = () => {
- const handler = useArrayPaginator(async () => await ioc.contractService.getTransferList(), {
+ const handler = useArrayPaginator(async () => await ioc.paymentGatewayService.getTransferList(), {
searchHandler: (rows: IRowData[], search: string) => {
return rows.filter(({ data, sender }) => (data + sender).toLowerCase().includes(search.toLowerCase()))
},
diff --git a/packages/client/src/pages/ConnectPage.tsx b/packages/client/src/pages/ConnectPage.tsx
index 793c05b..7dfdef3 100644
--- a/packages/client/src/pages/ConnectPage.tsx
+++ b/packages/client/src/pages/ConnectPage.tsx
@@ -27,10 +27,12 @@ const useStyles = makeStyles()((theme) => ({
display: 'flex',
alignItems: 'center',
flexDirection: 'column',
- gap: 15,
+ gap: 20,
padding: 15,
},
container: {
+ position: "relative",
+ overflow: "hidden",
minWidth: 375,
maxWidth: 375,
padding: 15,
@@ -49,7 +51,7 @@ export const ConnectPage = () => {
-
+
Please, connect your MetaMask wallet 😃