├── .browserslistrc
├── .editorconfig
├── .github
└── workflows
│ └── gh-pages.yml
├── .gitignore
├── .vscode
├── extensions.json
├── launch.json
└── tasks.json
├── LICENSE
├── README.md
├── angular.json
├── karma.conf.js
├── package-lock.json
├── package.json
├── src
├── app
│ ├── app-routing.module.ts
│ ├── app.component.html
│ ├── app.component.scss
│ ├── app.component.spec.ts
│ ├── app.component.ts
│ └── app.module.ts
├── assets
│ ├── .gitkeep
│ ├── icon-256x256.png
│ └── icon-48x48.png
├── blazor
│ └── dapp-wasm
│ │ ├── Blockcore.AtomicSwaps.BlockcoreDns
│ │ ├── Blockcore.AtomicSwaps.BlockcoreDns.csproj
│ │ ├── BlockcoreDns.razor
│ │ ├── BlockcoreDns.razor.cs
│ │ ├── BlockcoreDnsService.cs
│ │ ├── IBlockcoreDnsService.cs
│ │ ├── Models
│ │ │ ├── DnsResult.cs
│ │ │ ├── DnsServices.cs
│ │ │ └── NsResult.cs
│ │ ├── ServiceCollectionExtensions.cs
│ │ ├── Toolkit
│ │ │ └── JsonToolKit.cs
│ │ └── _Imports.razor
│ │ ├── Blockcore.AtomicSwaps.BlockcoreWallet
│ │ ├── Blockcore.AtomicSwaps.BlockcoreWallet.csproj
│ │ ├── BlockcoreWallet.razor
│ │ ├── BlockcoreWallet.razor.cs
│ │ ├── BlockcoreWalletConnector.cs
│ │ ├── BlockcoreWalletModels.cs
│ │ ├── Exceptions
│ │ │ ├── NoBlockcoreWalletException.cs
│ │ │ └── UserDeniedException.cs
│ │ ├── IBlockcoreWalletConnector.cs
│ │ ├── ServiceCollectionExtensions.cs
│ │ ├── _Imports.razor
│ │ └── wwwroot
│ │ │ └── blockcoreWallet.js
│ │ ├── Blockcore.MetaMask
│ │ ├── Blockcore.MetaMask.csproj
│ │ ├── Enums
│ │ │ └── Chain.cs
│ │ ├── Exceptions
│ │ │ ├── NoMetaMaskException.cs
│ │ │ └── UserDeniedException.cs
│ │ ├── Extensions
│ │ │ ├── HexExtensions.cs
│ │ │ └── SendTransactionExtensions.cs
│ │ ├── IMetaMaskService.cs
│ │ ├── MetaMaskService.cs
│ │ ├── Metamask.razor
│ │ ├── Metamask.razor.cs
│ │ ├── Models
│ │ │ └── TypedDataPayload.cs
│ │ ├── ServiceCollectionExtensions.cs
│ │ ├── _Imports.razor
│ │ └── wwwroot
│ │ │ └── metaMask.js
│ │ ├── dapp-wasm.sln
│ │ └── dapp-wasm
│ │ ├── App.razor
│ │ ├── Data
│ │ ├── BlockData.cs
│ │ ├── BtcDecimalJsonConverter.cs
│ │ ├── ToStringJsonConverter.cs
│ │ └── TransactionModel.cs
│ │ ├── Networks
│ │ ├── BitcoinMain.cs
│ │ ├── CityMain.cs
│ │ ├── Networks.cs
│ │ └── StraxMain.cs
│ │ ├── Pages
│ │ ├── FetchData.razor
│ │ ├── GenerateAddresses.razor
│ │ ├── Index.razor
│ │ ├── ParseData.razor
│ │ └── SignMessage.razor
│ │ ├── Program.cs
│ │ ├── Properties
│ │ └── launchSettings.json
│ │ ├── Shared
│ │ ├── MainLayout.razor
│ │ ├── MainLayout.razor.css
│ │ ├── NavMenu.razor
│ │ └── NavMenu.razor.css
│ │ ├── _Imports.razor
│ │ ├── dapp-wasm.csproj
│ │ └── wwwroot
│ │ ├── css
│ │ ├── app.css
│ │ ├── bootstrap
│ │ │ ├── bootstrap.min.css
│ │ │ └── bootstrap.min.css.map
│ │ └── open-iconic
│ │ │ ├── FONT-LICENSE
│ │ │ ├── ICON-LICENSE
│ │ │ ├── README.md
│ │ │ └── font
│ │ │ ├── css
│ │ │ └── open-iconic-bootstrap.min.css
│ │ │ └── fonts
│ │ │ ├── open-iconic.eot
│ │ │ ├── open-iconic.otf
│ │ │ ├── open-iconic.svg
│ │ │ ├── open-iconic.ttf
│ │ │ └── open-iconic.woff
│ │ ├── favicon.png
│ │ ├── icon-192.png
│ │ ├── index.html
│ │ └── sample-data
│ │ └── weather.json
├── environments
│ ├── environment.prod.ts
│ └── environment.ts
├── favicon.ico
├── index.html
├── main.ts
├── polyfills.ts
├── styles.css
├── styles.scss
├── test.ts
└── webpack.config.js
├── tsconfig.app.json
├── tsconfig.json
└── tsconfig.spec.json
/.browserslistrc:
--------------------------------------------------------------------------------
1 | # This file is used by the build system to adjust CSS and JS output to support the specified browsers below.
2 | # For additional information regarding the format and rule options, please see:
3 | # https://github.com/browserslist/browserslist#queries
4 |
5 | # For the full list of supported browsers by the Angular framework, please see:
6 | # https://angular.io/guide/browser-support
7 |
8 | # You can see what browsers were selected by your queries by running:
9 | # npx browserslist
10 |
11 | last 1 Chrome version
12 | last 1 Firefox version
13 | last 2 Edge major versions
14 | last 2 Safari major versions
15 | last 2 iOS major versions
16 | Firefox ESR
17 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # Editor configuration, see https://editorconfig.org
2 | root = true
3 |
4 | [*]
5 | charset = utf-8
6 | indent_style = space
7 | indent_size = 2
8 | insert_final_newline = true
9 | trim_trailing_whitespace = true
10 | max_line_length = 240
11 |
12 | [*.ts]
13 | quote_type = single
14 |
15 | [*.md]
16 | max_line_length = off
17 | trim_trailing_whitespace = false
18 |
--------------------------------------------------------------------------------
/.github/workflows/gh-pages.yml:
--------------------------------------------------------------------------------
1 | name: github pages
2 |
3 | on:
4 | push:
5 | branches:
6 | - main
7 | workflow_dispatch:
8 |
9 | jobs:
10 | deploy:
11 | runs-on: ubuntu-latest
12 | steps:
13 | - uses: actions/checkout@v2
14 |
15 | - name: Setup Node
16 | uses: actions/setup-node@v1
17 | with:
18 | node-version: '18.x'
19 |
20 | - name: Cache dependencies
21 | uses: actions/cache@v1
22 | with:
23 | path: ~/.npm
24 | key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
25 | restore-keys: |
26 | ${{ runner.os }}-node-
27 | - run: npm ci
28 | - run: npm run build
29 |
30 | - name: Deploy
31 | uses: peaceiris/actions-gh-pages@v3
32 | with:
33 | github_token: ${{ secrets.GITHUB_TOKEN }}
34 | publish_dir: ./dist/dapp-sample
35 | cname: dapp.blockcore.net
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See http://help.github.com/ignore-files/ for more about ignoring files.
2 |
3 | # Compiled output
4 | /dist
5 | /tmp
6 | /out-tsc
7 | /bazel-out
8 |
9 | # Node
10 | /node_modules
11 | npm-debug.log
12 | yarn-error.log
13 |
14 | # IDEs and editors
15 | .idea/
16 | .project
17 | .classpath
18 | .c9/
19 | *.launch
20 | .settings/
21 | *.sublime-workspace
22 |
23 | # Visual Studio Code
24 | .vscode/*
25 | !.vscode/settings.json
26 | !.vscode/tasks.json
27 | !.vscode/launch.json
28 | !.vscode/extensions.json
29 | .history/*
30 |
31 | # Miscellaneous
32 | /.angular/cache
33 | .sass-cache/
34 | /connect.lock
35 | /coverage
36 | /libpeerconnection.log
37 | testem.log
38 | /typings
39 |
40 | # System files
41 | .DS_Store
42 | Thumbs.db
43 | /src/blazor/dapp-wasm/.vs/dapp-wasm
44 | /src/blazor/dapp-wasm/dapp-wasm/obj
45 | /src/blazor/dapp-wasm/.vs/ProjectEvaluation
46 | /src/blazor/dapp-wasm/dapp-wasm/bin/Debug/net7.0
47 | /src/blazor/dapp-wasm/Blockcore.MetaMask/bin
48 | /src/blazor/dapp-wasm/Blockcore.MetaMask/obj
49 | /src/blazor/dapp-wasm/Blockcore.AtomicSwaps.BlockcoreDns/obj
50 | /src/blazor/dapp-wasm/Blockcore.AtomicSwaps.BlockcoreWallet/obj
51 | /src/blazor/dapp-wasm/Blockcore.AtomicSwaps.BlockcoreWallet/Script/node_modules/.bin
52 | /src/blazor/dapp-wasm/Blockcore.AtomicSwaps.BlockcoreWallet/Script/node_modules
53 |
--------------------------------------------------------------------------------
/.vscode/extensions.json:
--------------------------------------------------------------------------------
1 | {
2 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=827846
3 | "recommendations": ["angular.ng-template", "esbenp.prettier-vscode"]
4 | }
5 |
--------------------------------------------------------------------------------
/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
3 | "version": "0.2.0",
4 | "configurations": [
5 | {
6 | "name": "ng serve",
7 | "type": "pwa-chrome",
8 | "request": "launch",
9 | "preLaunchTask": "npm: start",
10 | "url": "http://localhost:4200/"
11 | },
12 | {
13 | "name": "ng test",
14 | "type": "chrome",
15 | "request": "launch",
16 | "preLaunchTask": "npm: test",
17 | "url": "http://localhost:9876/debug.html"
18 | }
19 | ]
20 | }
21 |
--------------------------------------------------------------------------------
/.vscode/tasks.json:
--------------------------------------------------------------------------------
1 | {
2 | // For more information, visit: https://go.microsoft.com/fwlink/?LinkId=733558
3 | "version": "2.0.0",
4 | "tasks": [
5 | {
6 | "type": "npm",
7 | "script": "start",
8 | "isBackground": true,
9 | "problemMatcher": {
10 | "owner": "typescript",
11 | "pattern": "$tsc",
12 | "background": {
13 | "activeOnStart": true,
14 | "beginsPattern": {
15 | "regexp": "(.*?)"
16 | },
17 | "endsPattern": {
18 | "regexp": "bundle generation complete"
19 | }
20 | }
21 | }
22 | },
23 | {
24 | "type": "npm",
25 | "script": "test",
26 | "isBackground": true,
27 | "problemMatcher": {
28 | "owner": "typescript",
29 | "pattern": "$tsc",
30 | "background": {
31 | "activeOnStart": true,
32 | "beginsPattern": {
33 | "regexp": "(.*?)"
34 | },
35 | "endsPattern": {
36 | "regexp": "bundle generation complete"
37 | }
38 | }
39 | }
40 | }
41 | ]
42 | }
43 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 Blockcore
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Blockcore DApp Sample
2 |
3 | Decentralized application (DApp) that demonstrates interaction from third party apps and Blockcore ecosystem
4 |
5 | ## Resources
6 |
7 | [Verifiable Credentials Data Model v2.0](https://w3c.github.io/vc-data-model/)
8 |
9 | [Verifiable Credentials Implementation Guidelines 1.0](https://w3c.github.io/vc-imp-guide/)
10 |
11 | ## Inspiration
12 |
13 | This app will have similar features as the E2E Test Dapp for MetaMask: [https://metamask.github.io/test-dapp/](https://metamask.github.io/test-dapp/)
14 |
15 | This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 14.2.7.
16 |
17 | ## Development server
18 |
19 | Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The application will automatically reload if you change any of the source files.
20 |
21 | ## Code scaffolding
22 |
23 | Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`.
24 |
25 | ## Build
26 |
27 | Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory.
28 |
29 | ## Running unit tests
30 |
31 | Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io).
32 |
33 | ## Running end-to-end tests
34 |
35 | Run `ng e2e` to execute the end-to-end tests via a platform of your choice. To use this command, you need to first add a package that implements end-to-end testing capabilities.
36 |
37 | ## Further help
38 |
39 | To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI Overview and Command Reference](https://angular.io/cli) page.
40 |
--------------------------------------------------------------------------------
/angular.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "./node_modules/@angular/cli/lib/config/schema.json",
3 | "version": 1,
4 | "newProjectRoot": "projects",
5 | "projects": {
6 | "dapp-sample": {
7 | "projectType": "application",
8 | "schematics": {
9 | "@schematics/angular:component": {
10 | "style": "scss"
11 | }
12 | },
13 | "root": "",
14 | "sourceRoot": "src",
15 | "prefix": "app",
16 | "architect": {
17 | "build": {
18 | "builder": "@angular-builders/custom-webpack:browser",
19 | "options": {
20 | "customWebpackConfig": {
21 | "path": "./src/webpack.config.js",
22 | "mergeRules": {
23 | "externals": "replace"
24 | }
25 | },
26 | "outputPath": "dist/dapp-sample",
27 | "index": "src/index.html",
28 | "main": "src/main.ts",
29 | "polyfills": "src/polyfills.ts",
30 | "tsConfig": "tsconfig.app.json",
31 | "inlineStyleLanguage": "scss",
32 | "assets": [
33 | "src/favicon.ico",
34 | "src/assets"
35 | ],
36 | "styles": [
37 | "./node_modules/@angular/material/prebuilt-themes/pink-bluegrey.css",
38 | "src/styles.scss"
39 | ],
40 | "scripts": []
41 | },
42 | "configurations": {
43 | "production": {
44 | "budgets": [
45 | {
46 | "type": "initial",
47 | "maximumWarning": "500kb",
48 | "maximumError": "1mb"
49 | },
50 | {
51 | "type": "anyComponentStyle",
52 | "maximumWarning": "2kb",
53 | "maximumError": "4kb"
54 | }
55 | ],
56 | "fileReplacements": [
57 | {
58 | "replace": "src/environments/environment.ts",
59 | "with": "src/environments/environment.prod.ts"
60 | }
61 | ],
62 | "outputHashing": "all"
63 | },
64 | "development": {
65 | "buildOptimizer": false,
66 | "optimization": false,
67 | "vendorChunk": true,
68 | "extractLicenses": false,
69 | "sourceMap": true,
70 | "namedChunks": true
71 | }
72 | },
73 | "defaultConfiguration": "production"
74 | },
75 | "serve": {
76 | "builder": "@angular-builders/custom-webpack:dev-server",
77 | "configurations": {
78 | "production": {
79 | "browserTarget": "dapp-sample:build:production"
80 | },
81 | "development": {
82 | "browserTarget": "dapp-sample:build:development"
83 | }
84 | },
85 | "defaultConfiguration": "development"
86 | },
87 | "extract-i18n": {
88 | "builder": "@angular-builders/custom-webpack:extract-i18n",
89 | "options": {
90 | "browserTarget": "dapp-sample:build"
91 | }
92 | },
93 | "test": {
94 | "builder": "@angular-builders/custom-webpack:karma",
95 | "options": {
96 | "customWebpackConfig": {
97 | "path": "./src/webpack.config.js",
98 | "mergeRules": {
99 | "externals": "replace"
100 | }
101 | },
102 | "main": "src/test.ts",
103 | "polyfills": "src/polyfills.ts",
104 | "tsConfig": "tsconfig.spec.json",
105 | "karmaConfig": "karma.conf.js",
106 | "inlineStyleLanguage": "scss",
107 | "assets": [
108 | "src/favicon.ico",
109 | "src/assets"
110 | ],
111 | "styles": [
112 | "./node_modules/@angular/material/prebuilt-themes/pink-bluegrey.css",
113 | "src/styles.scss"
114 | ],
115 | "scripts": []
116 | }
117 | }
118 | }
119 | }
120 | }
121 | }
122 |
--------------------------------------------------------------------------------
/karma.conf.js:
--------------------------------------------------------------------------------
1 | // Karma configuration file, see link for more information
2 | // https://karma-runner.github.io/1.0/config/configuration-file.html
3 |
4 | module.exports = function (config) {
5 | config.set({
6 | basePath: '',
7 | frameworks: ['jasmine', '@angular-devkit/build-angular'],
8 | plugins: [
9 | require('karma-jasmine'),
10 | require('karma-chrome-launcher'),
11 | require('karma-jasmine-html-reporter'),
12 | require('karma-coverage'),
13 | require('@angular-devkit/build-angular/plugins/karma')
14 | ],
15 | client: {
16 | jasmine: {
17 | // you can add configuration options for Jasmine here
18 | // the possible options are listed at https://jasmine.github.io/api/edge/Configuration.html
19 | // for example, you can disable the random execution with `random: false`
20 | // or set a specific seed with `seed: 4321`
21 | },
22 | clearContext: false // leave Jasmine Spec Runner output visible in browser
23 | },
24 | jasmineHtmlReporter: {
25 | suppressAll: true // removes the duplicated traces
26 | },
27 | coverageReporter: {
28 | dir: require('path').join(__dirname, './coverage/dapp-sample'),
29 | subdir: '.',
30 | reporters: [
31 | { type: 'html' },
32 | { type: 'text-summary' }
33 | ]
34 | },
35 | reporters: ['progress', 'kjhtml'],
36 | port: 9876,
37 | colors: true,
38 | logLevel: config.LOG_INFO,
39 | autoWatch: true,
40 | browsers: ['Chrome'],
41 | singleRun: false,
42 | restartOnFileChange: true
43 | });
44 | };
45 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "dapp-sample",
3 | "version": "0.0.0",
4 | "scripts": {
5 | "ng": "ng",
6 | "start": "ng serve",
7 | "build": "ng build",
8 | "watch": "ng build --watch --configuration development",
9 | "test": "ng test"
10 | },
11 | "private": true,
12 | "dependencies": {
13 | "@angular/animations": "^14.2.0",
14 | "@angular/cdk": "^14.2.6",
15 | "@angular/common": "^14.2.0",
16 | "@angular/compiler": "^14.2.0",
17 | "@angular/core": "^14.2.0",
18 | "@angular/forms": "^14.2.0",
19 | "@angular/material": "^14.2.6",
20 | "@angular/platform-browser": "^14.2.0",
21 | "@angular/platform-browser-dynamic": "^14.2.0",
22 | "@angular/router": "^14.2.0",
23 | "@blockcore/did-resolver": "^0.0.3",
24 | "@blockcore/provider": "^0.0.12",
25 | "@noble/hashes": "^1.1.5",
26 | "@noble/secp256k1": "^1.7.0",
27 | "bitcoinjs-message": "^2.2.0",
28 | "did-resolver": "^4.0.1",
29 | "rxjs": "~7.5.0",
30 | "stream-browserify": "^3.0.0",
31 | "tslib": "^2.3.0",
32 | "uuid": "^9.0.0",
33 | "zone.js": "~0.11.4"
34 | },
35 | "devDependencies": {
36 | "@angular-builders/custom-webpack": "^14.0.1",
37 | "@angular-devkit/build-angular": "^14.2.7",
38 | "@angular/cli": "~14.2.7",
39 | "@angular/compiler-cli": "^14.2.0",
40 | "@types/jasmine": "~4.0.0",
41 | "jasmine-core": "~4.3.0",
42 | "karma": "~6.4.0",
43 | "karma-chrome-launcher": "~3.1.0",
44 | "karma-coverage": "~2.2.0",
45 | "karma-jasmine": "~5.1.0",
46 | "karma-jasmine-html-reporter": "~2.0.0",
47 | "typescript": "~4.7.2"
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/src/app/app-routing.module.ts:
--------------------------------------------------------------------------------
1 | import { NgModule } from '@angular/core';
2 | import { RouterModule, Routes } from '@angular/router';
3 |
4 | const routes: Routes = [];
5 |
6 | @NgModule({
7 | imports: [RouterModule.forRoot(routes)],
8 | exports: [RouterModule]
9 | })
10 | export class AppRoutingModule { }
11 |
--------------------------------------------------------------------------------
/src/app/app.component.scss:
--------------------------------------------------------------------------------
1 | .example-spacer {
2 | width: 50px;
3 | }
4 |
5 | .logo {
6 | margin-left: auto;
7 | margin-right: auto;
8 | width: 100%;
9 | max-width: 256px;
10 | }
11 |
12 | .actions button {
13 | margin-right: 0.6em;
14 | margin-bottom: 0.6em;
15 | }
16 |
17 | .input-full-width {
18 | width: 100%;
19 | }
20 |
21 | .card {
22 | margin-bottom: 1em;
23 | }
24 |
25 | .long {
26 | word-wrap: break-word;
27 | }
28 |
29 | .blockcore-logo {
30 | margin: 0 4px 3px 0;
31 | height: 48px;
32 | vertical-align: middle;
33 | }
34 | .fill-remaining-space {
35 | flex: 1 1 auto;
36 | }
37 |
38 |
--------------------------------------------------------------------------------
/src/app/app.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { TestBed } from '@angular/core/testing';
2 | import { RouterTestingModule } from '@angular/router/testing';
3 | import { AppComponent } from './app.component';
4 |
5 | describe('AppComponent', () => {
6 | beforeEach(async () => {
7 | await TestBed.configureTestingModule({
8 | imports: [
9 | RouterTestingModule
10 | ],
11 | declarations: [
12 | AppComponent
13 | ],
14 | }).compileComponents();
15 | });
16 |
17 | it('should create the app', () => {
18 | const fixture = TestBed.createComponent(AppComponent);
19 | const app = fixture.componentInstance;
20 | expect(app).toBeTruthy();
21 | });
22 |
23 | it(`should have as title 'dapp-sample'`, () => {
24 | const fixture = TestBed.createComponent(AppComponent);
25 | const app = fixture.componentInstance;
26 | expect(app.title).toEqual('dapp-sample');
27 | });
28 |
29 | it('should render title', () => {
30 | const fixture = TestBed.createComponent(AppComponent);
31 | fixture.detectChanges();
32 | const compiled = fixture.nativeElement as HTMLElement;
33 | expect(compiled.querySelector('.content span')?.textContent).toContain('dapp-sample app is running!');
34 | });
35 | });
36 |
--------------------------------------------------------------------------------
/src/app/app.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit } from '@angular/core';
2 | import { WebProvider } from '@blockcore/provider';
3 | import * as bitcoinMessage from 'bitcoinjs-message';
4 | const { v4: uuidv4 } = require('uuid');
5 | import { DIDResolutionOptions, Resolver } from 'did-resolver';
6 | import is from '@blockcore/did-resolver';
7 | import * as secp256k1 from '@noble/secp256k1';
8 | import { sha256 } from '@noble/hashes/sha256';
9 |
10 | @Component({
11 | selector: 'app-root',
12 | templateUrl: './app.component.html',
13 | styleUrls: ['./app.component.scss'],
14 | })
15 | export class AppComponent implements OnInit {
16 | title = 'dapp-sample';
17 | provider?: WebProvider;
18 |
19 | signingText: string = 'Hello World';
20 | signingJson: string = '{ "id": 5, "text": "Hello World" }';
21 |
22 | signedTextSignature?: string;
23 | signedTextKey?: string;
24 | signedTextNetwork?: string;
25 | signedTextValidSignature?: boolean;
26 |
27 | // rawPSBT is a Base64-encoded string representing the PSBT
28 | rawPSBT: string =
29 | '70736274ff010052020000000197ad5142d4b313e39d06320d52aa608c06525dd3aad59f3033306cc7dae20ecc0000000000ffffffff01e803000000000000160014d85c2b71d0060b09c9886aeb815e50991dda124d00000000000100e10200000000010179c41695a3be63ad012bdfb3e4fa1e1ed529b546671ee175eca105193940394f0100000000fdffffff02e8030000000000001976a914ad0e5d23994e4aedea78930662c1488ca2544efd88ac4232000000000000160014e3b767040067973106e977f46b9b449fd67f92120247304402200268c49c6e89dd3b8d59b78e35612ec2685730bc471aba3a49edd58d08d089cb02203e12e950cd59f7e0af3da88e33e221247c9ccada7a43ee4910e4d589245c37c80121026b0945d725e12b3c8fe123858bfb4d6da697329b8fbe5e02ee1e0b6ccdf3be79efd42b002206030272524d872070574dc2d183060efb8e691b0d3523e965644384446ec53190f4186c127a852c000080010000800000008000000000000000000000';
30 |
31 | signedPsbtSignature?: string;
32 |
33 | signedJsonSignature?: string;
34 | signedJsonKey?: string;
35 | signedJsonNetwork?: string;
36 | signedJsonValidSignature?: boolean;
37 |
38 | paymentRequestAmount = 2;
39 | paymentVerificationRequestAmount = 5;
40 | paymentVerificationTransactionId: string | undefined = undefined;
41 | paymentTransactionId: string | undefined = undefined;
42 |
43 | didSupportedMethodsResponse?: string[];
44 | didRequestResponse: any;
45 |
46 | wallet: any;
47 | nostrPublicKey = '';
48 | nostrSignedEvent = '';
49 | nostrRelays?: string[];
50 |
51 | nostrEvent = {
52 | created_at: Date.now(),
53 | kind: 1,
54 | tags: [],
55 | content: 'This is my nostr message',
56 | pubkey: '',
57 | };
58 |
59 | // vcSubject = 'did:is:';
60 | vcType = 'EmailVerification';
61 | vcID = uuidv4();
62 | vcClaim = '{ "id": "did:is:0f254e55a2633d468e92aa7dd5a76c0c9101fab8e282c8c20b3fefde0d68f217", "sameAs": "mail@mail.com" }';
63 | vc: string | undefined | null = null;
64 |
65 | networks = [
66 | { name: 'Any (user selected)', id: '' },
67 | { name: 'Bitcoin', id: 'BTC' },
68 | { name: 'City Chain', id: 'CITY' },
69 | { name: 'Stratis', id: 'STRAX' },
70 | { name: 'x42', id: 'X42' },
71 | ];
72 |
73 | network = 'BTC';
74 |
75 | constructor() {}
76 |
77 | ngOnInit(): void {
78 | console.log('This will be false:', this.isBlockcoreInstalled());
79 |
80 | setTimeout(() => {
81 | console.log('This will hopefully be true:', this.isBlockcoreInstalled());
82 | }, 250);
83 | }
84 |
85 | isBlockcoreInstalled = () => {
86 | const { blockcore } = globalThis as any;
87 | return Boolean(blockcore);
88 | };
89 |
90 | async initialize() {
91 | // Creating the WebProvider will perform multiple network requests to
92 | // get all known blockchain APIs.
93 | this.provider = await WebProvider.Create();
94 | this.provider.setNetwork(this.network);
95 |
96 | this.vcID = uuidv4();
97 | }
98 |
99 | async getNostrPublicKey() {
100 | const gt = globalThis as any;
101 |
102 | // Use nostr directly on global, similar to how most Nostr app will interact with the provider.
103 | const pubKey = await gt.nostr.getPublicKey();
104 |
105 | this.nostrPublicKey = pubKey;
106 |
107 | this.nostrEvent.pubkey = this.nostrPublicKey;
108 | }
109 |
110 | serializeEvent(evt: any): string {
111 | return JSON.stringify([0, evt.pubkey, evt.created_at, evt.kind, evt.tags, evt.content]);
112 | }
113 |
114 | getEventHash(event: Event): string {
115 | const utf8Encoder = new TextEncoder();
116 | let eventHash = sha256(utf8Encoder.encode(this.serializeEvent(event)));
117 | return secp256k1.utils.bytesToHex(eventHash);
118 | }
119 |
120 | async nostrSignEvent(event: any) {
121 | const gt = globalThis as any;
122 |
123 | event.id = this.getEventHash(event);
124 |
125 | try {
126 | // Use nostr directly on global, similar to how most Nostr app will interact with the provider.
127 | const signedEvent = await gt.nostr.signEvent(event);
128 | this.nostrSignedEvent = signedEvent;
129 | } catch (err: any) {
130 | console.error(err);
131 | this.nostrSignedEvent = err.toString();
132 | }
133 | }
134 |
135 | async getNostrPublicRelays() {
136 | const gt = globalThis as any;
137 | const relays = await gt.nostr.getRelays();
138 | this.nostrRelays = relays;
139 | }
140 |
141 | async nostrEncrypt() {
142 | this.nostrCipher = null;
143 |
144 | const gt = globalThis as any;
145 | const cipher = await gt.nostr.nip04.encrypt(this.nostrPublicKey, this.nostrEvent.content);
146 | this.nostrCipher = cipher;
147 | }
148 |
149 | nostrCipher = null;
150 | nostrDecrypted = null;
151 |
152 | async nostrDecrypt() {
153 | this.nostrDecrypted = null;
154 |
155 | const gt = globalThis as any;
156 | const content = await gt.nostr.nip04.decrypt(this.nostrPublicKey, this.nostrCipher);
157 | this.nostrDecrypted = content;
158 | }
159 |
160 | transactionResult: any = undefined;
161 |
162 | async sendTransaction() {
163 | const result: any = await this.provider!.request({
164 | method: 'transaction.send',
165 | params: [
166 | {
167 | recipients: [
168 | { hint: 'Swap', address: 'CMrc2rFsPd9VnxPiHvN2wHFVNfNd9vY8Ze', amount: 100000000 },
169 | { hint: 'Fee Service', address: 'CMrc2rFsPd9VnxPiHvN2wHFVNfNd9vY8Ze', amount: 20000000 },
170 | ],
171 | // data: 'op_return',
172 | feeRate: 'medium',
173 | network: this.provider?.indexer.network,
174 | },
175 | ],
176 | });
177 |
178 | console.log('Result:', result);
179 |
180 | this.transactionResult = result;
181 |
182 | // this.signedTextKey = result.key;
183 | // this.signedTextSignature = result.response.signature;
184 | // this.signedTextNetwork = result.network;
185 | // this.signedTextValidSignature = bitcoinMessage.verify(value, result.key, result.response.signature);
186 | }
187 |
188 | async atomicSwapKey() {
189 | const result: any = await this.provider!.request({
190 | method: 'atomicswaps.key',
191 | params: [
192 | {
193 | walletId: this.wallet.response.wallet.id,
194 | accountId: this.atomicSwapAccountId,
195 | network: this.provider?.indexer.network,
196 | },
197 | ],
198 | });
199 |
200 | console.log('Result:', result);
201 | this.atomicSwapPublicKey = result.response.publicKey;
202 | }
203 |
204 | atomicSwapPublicKey?: string;
205 | atomicSwapSecretKey?: string;
206 |
207 | async atomicSwapSecret() {
208 | const result: any = await this.provider!.request({
209 | method: 'atomicswaps.secret',
210 | params: [
211 | {
212 | walletId: this.wallet.response.wallet.id,
213 | accountId: this.atomicSwapAccountId,
214 | network: this.provider?.indexer.network,
215 | message: '1', // "sessionid"?
216 | },
217 | ],
218 | });
219 |
220 | console.log('Result:', result);
221 | this.atomicSwapSecretKey = result.response.secret;
222 | }
223 |
224 | async atomicSwapSend() {
225 | const result: any = await this.provider!.request({
226 | method: 'atomicswaps.send',
227 | params: [
228 | {
229 | walletId: this.wallet.response.wallet.id,
230 | accountId: this.atomicSwapAccountId,
231 | network: this.provider?.indexer.network,
232 | },
233 | ],
234 | });
235 |
236 | console.log('Result:', result);
237 | }
238 |
239 | async signMessageAnyAccount(value: string) {
240 | const result: any = await this.provider!.request({
241 | method: 'signMessage',
242 | params: [{ message: value, network: this.provider?.indexer.network }],
243 | });
244 | console.log('Result:', result);
245 |
246 | this.signedTextKey = result.key;
247 | this.signedTextSignature = result.response.signature;
248 | this.signedTextNetwork = result.network;
249 | this.signedTextValidSignature = bitcoinMessage.verify(value, result.key, result.response.signature);
250 | }
251 |
252 | async signPsbtAnyAccount(value: string) {
253 | const result: any = await this.provider!.request({
254 | method: 'signPsbt',
255 | params: [{ message: value, network: this.provider?.indexer.network }],
256 | });
257 | console.log('Result:', result);
258 |
259 | this.signedPsbtSignature = result.response.signature;
260 | }
261 |
262 | async signMessageAnyAccountJson(value: string) {
263 | const message = JSON.parse(value);
264 |
265 | const result: any = await this.provider!.request({
266 | method: 'signMessage',
267 | params: [{ message: message, network: this.provider?.indexer.network }],
268 | });
269 |
270 | console.log('Result:', result);
271 |
272 | this.signedJsonKey = result.key;
273 | this.signedJsonSignature = result.response.signature;
274 | this.signedJsonNetwork = result.network;
275 | const preparedMessage = JSON.stringify(message);
276 | this.signedJsonValidSignature = bitcoinMessage.verify(preparedMessage, result.key, result.response.signature);
277 | }
278 |
279 | async connect() {
280 | const challenge = uuidv4();
281 |
282 | try {
283 | var result: any = await this.provider!.request({
284 | method: 'wallets',
285 | params: [
286 | {
287 | challenge: challenge,
288 | // reason: 'Sample app want you to sign a verifiable credential with any of your DIDs.',
289 | },
290 | ],
291 | });
292 |
293 | console.log('Result:', result);
294 | this.wallet = result;
295 | } catch (err) {
296 | console.error(err);
297 | }
298 | }
299 |
300 | atomicSwapAccountId?: string;
301 |
302 | getAccounts() {}
303 |
304 | async paymentVerificationRequest(network: string, amount: number) {
305 | const paymentId = uuidv4();
306 |
307 | try {
308 | var result: any = await this.provider!.request({
309 | method: 'payment',
310 | params: [
311 | {
312 | network: network.toLowerCase(),
313 | amount: amount,
314 | address: 'CRp1q2hdFN5e1hVEEkFY3egyD2cT9ed6S3',
315 | label: 'City Chain Registry',
316 | message: 'Please make the initial payment for crypto company registration',
317 | id: paymentId,
318 | },
319 | ],
320 | });
321 |
322 | console.log('PAYMENT VERIFICATION REQUEST CLOSED...');
323 | console.log('Result:', result);
324 |
325 | this.paymentVerificationTransactionId = result.transactionId;
326 | } catch (err) {
327 | console.error(err);
328 | }
329 | }
330 |
331 | async paymentRequest(network: string, amount: number) {
332 | try {
333 | var result: any = await this.provider!.request({
334 | method: 'payment',
335 | params: [
336 | {
337 | network: network.toLowerCase(),
338 | amount: amount,
339 | address: 'Ccoquhaae7u6ASqQ5BiYueASz8EavUXrKn',
340 | label: 'Your Local Info',
341 | message: 'Invoice Number 5',
342 | data: 'MzExMzUzNDIzNDY',
343 | id: '4324',
344 | },
345 | ],
346 | });
347 |
348 | console.log('Result:', result);
349 | this.paymentTransactionId = result.transactionId;
350 | } catch (err) {
351 | console.error(err);
352 | }
353 | }
354 |
355 | async paymentRequestCity() {
356 | try {
357 | var result = await this.provider!.request({
358 | method: 'payment',
359 | params: [
360 | {
361 | network: 'city',
362 | amount: 10.5,
363 | address: 'Ccoquhaae7u6ASqQ5BiYueASz8EavUXrKn',
364 | label: 'Your Local Info',
365 | message: 'Invoice Number 5',
366 | data: 'MzExMzUzNDIzNDY',
367 | id: '4324',
368 | },
369 | ],
370 | });
371 |
372 | console.log('Result:', result);
373 | } catch (err) {
374 | console.error(err);
375 | }
376 | }
377 |
378 | async request(method: string, params?: object | unknown[]) {
379 | if (!params) {
380 | params = [];
381 | }
382 |
383 | const result: any = await this.provider!.request({
384 | method: method,
385 | params: params,
386 | });
387 | console.log('Result:', result);
388 |
389 | return result;
390 | }
391 |
392 | async didSupportedMethods() {
393 | const result = await this.request('did.supportedMethods');
394 | this.didSupportedMethodsResponse = result.response;
395 | }
396 |
397 | async didRequest(methods: string[]) {
398 | const result = await this.request('did.request', [
399 | {
400 | challenge: 'fc0949c4-fd9c-4825-b18d-79348e358156',
401 | methods: methods,
402 | reason: 'Sample app need access to any of your DIDs.',
403 | },
404 | ]);
405 |
406 | this.didRequestResponse = result;
407 | }
408 |
409 | vcRequestResponse: any;
410 | didLookup = 'did:is:0f254e55a2633d468e92aa7dd5a76c0c9101fab8e282c8c20b3fefde0d68f217';
411 | didLookupResponse: any | undefined;
412 |
413 | async resolveDid() {
414 | const isResolver = is.getResolver();
415 | const resolver = new Resolver(isResolver);
416 | this.didLookupResponse = await resolver.resolve(this.didLookup, {});
417 | }
418 |
419 | async vcRequest() {
420 | const result = await this.request('vc.request', [
421 | {
422 | challenge: 'fc0949c4-fd9c-4825-b18d-79348e358156',
423 | type: this.vcType,
424 | id: this.vcID,
425 | claim: this.vcClaim,
426 | reason: 'Sample app want you to sign a verifiable credential with any of your DIDs.',
427 | },
428 | ]);
429 |
430 | this.vcRequestResponse = result;
431 | }
432 |
433 | onNetworkChanged() {
434 | console.log(this.network);
435 | this.provider?.setNetwork(this.network);
436 | }
437 |
438 | async paymentRequestStrax() {
439 | try {
440 | var result = await this.provider!.request({
441 | method: 'payment',
442 | params: [
443 | {
444 | network: 'strax',
445 | amount: 2,
446 | address: 'Xcoquhaae7u6ASqQ5BiYueASz8EavUXrKn',
447 | label: 'Your Local Info',
448 | message: 'Invoice Number 5',
449 | data: 'MzExMzUzNDIzNDY',
450 | id: '4324',
451 | },
452 | ],
453 | });
454 |
455 | console.log('Result:', result);
456 | } catch (err) {
457 | console.error(err);
458 | }
459 | }
460 | }
461 |
--------------------------------------------------------------------------------
/src/app/app.module.ts:
--------------------------------------------------------------------------------
1 | import { NgModule } from '@angular/core';
2 | import { BrowserModule } from '@angular/platform-browser';
3 |
4 | import { AppRoutingModule } from './app-routing.module';
5 | import { AppComponent } from './app.component';
6 | import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
7 | import { MatToolbarModule } from '@angular/material/toolbar';
8 | import { MatIconModule } from '@angular/material/icon';
9 | import { MatButtonModule } from '@angular/material/button';
10 | import { MatCommonModule } from '@angular/material/core';
11 | import { MatCardModule } from '@angular/material/card';
12 | import { MatInputModule } from '@angular/material/input';
13 | import { MatFormFieldModule } from '@angular/material/form-field';
14 | import { FormsModule, ReactiveFormsModule } from '@angular/forms';
15 | import { MatSelectModule } from '@angular/material/select';
16 | import { MatTabsModule } from '@angular/material/tabs';
17 |
18 | @NgModule({
19 | declarations: [AppComponent],
20 | imports: [
21 | BrowserModule,
22 | FormsModule,
23 | MatTabsModule,
24 | ReactiveFormsModule,
25 | AppRoutingModule,
26 | BrowserAnimationsModule,
27 | MatCommonModule,
28 | MatSelectModule,
29 | MatToolbarModule,
30 | MatIconModule,
31 | MatButtonModule,
32 | MatCardModule,
33 | MatInputModule,
34 | MatFormFieldModule,
35 | ],
36 | providers: [],
37 | bootstrap: [AppComponent],
38 | })
39 | export class AppModule {}
40 |
--------------------------------------------------------------------------------
/src/assets/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/super-devninja/dapp-sample/c56c620048fafbe15117540e248ecd9de3d509ac/src/assets/.gitkeep
--------------------------------------------------------------------------------
/src/assets/icon-256x256.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/super-devninja/dapp-sample/c56c620048fafbe15117540e248ecd9de3d509ac/src/assets/icon-256x256.png
--------------------------------------------------------------------------------
/src/assets/icon-48x48.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/super-devninja/dapp-sample/c56c620048fafbe15117540e248ecd9de3d509ac/src/assets/icon-48x48.png
--------------------------------------------------------------------------------
/src/blazor/dapp-wasm/Blockcore.AtomicSwaps.BlockcoreDns/Blockcore.AtomicSwaps.BlockcoreDns.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net7.0
5 | True
6 | True
7 | Blockcore
8 | 1.0.0
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/src/blazor/dapp-wasm/Blockcore.AtomicSwaps.BlockcoreDns/BlockcoreDns.razor:
--------------------------------------------------------------------------------
1 | @page "/blockcoredns"
2 | @using Blockcore.AtomicSwaps.BlockcoreDns
3 | @using Microsoft.JSInterop
4 | @inject IJSRuntime JSRuntime;
5 |
6 | Blockcore Dns
7 |
8 |
Blockcore Dns!
9 |
10 |
11 | @blockcoreDnsUrl
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
46 |
--------------------------------------------------------------------------------
/src/blazor/dapp-wasm/Blockcore.AtomicSwaps.BlockcoreDns/BlockcoreDns.razor.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Threading.Tasks;
4 | using Blockcore.AtomicSwaps.BlockcoreDns.Models;
5 | using Microsoft.AspNetCore.Components;
6 |
7 | namespace Blockcore.AtomicSwaps.BlockcoreDns
8 | {
9 | public partial class BlockcoreDns : IDisposable
10 | {
11 | private bool disposedValue;
12 | [Inject]
13 | public IBlockcoreDnsService blockcoreDnsService { get; set; } = default!;
14 | public string? blockcoreDnsUrl { get; set; }
15 | public string? network { get; set; }
16 | public string? type { get; set; }
17 | public string textColor { get; set; } = "text-success";
18 |
19 | public IList services { get; set; }
20 |
21 | protected override Task OnInitializedAsync()
22 | {
23 | blockcoreDnsUrl = blockcoreDnsService.GetDnsServiceUrl();
24 | return Task.CompletedTask;
25 | }
26 |
27 | public async Task GetServicesByNetwork()
28 | {
29 | services = await blockcoreDnsService.GetServicesByNetwork(network);
30 | }
31 |
32 | public async Task GetServicesByType()
33 | {
34 | services = await blockcoreDnsService.GetServicesByType(type);
35 | }
36 |
37 | public async Task GetServicesByTypeAndNetwork()
38 | {
39 | services = await blockcoreDnsService.GetServicesByTypeAndNetwork(type,network);
40 | }
41 |
42 | protected virtual void Dispose(bool disposing)
43 | {
44 | if (!disposedValue)
45 | {
46 | if (disposing)
47 | {
48 | // TODO: dispose managed state (managed objects)
49 | }
50 |
51 | // TODO: free unmanaged resources (unmanaged objects) and override finalizer
52 | // TODO: set large fields to null
53 | disposedValue = true;
54 | }
55 | }
56 |
57 | public void Dispose()
58 | {
59 | // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
60 | Dispose(disposing: true);
61 | GC.SuppressFinalize(this);
62 | }
63 | }
64 |
65 |
66 | }
67 |
--------------------------------------------------------------------------------
/src/blazor/dapp-wasm/Blockcore.AtomicSwaps.BlockcoreDns/BlockcoreDnsService.cs:
--------------------------------------------------------------------------------
1 | using Blockcore.AtomicSwaps.BlockcoreDns.Models;
2 | using Blockcore.AtomicSwaps.BlockcoreDns.Toolkit;
3 | using System;
4 | using System.Collections.Generic;
5 | using System.Linq;
6 | using System.Threading.Tasks;
7 |
8 | namespace Blockcore.AtomicSwaps.BlockcoreDns
9 | {
10 | public class BlockcoreDnsService : IBlockcoreDnsService
11 | {
12 | private readonly string dnsServiceUrl = "https://chains.blockcore.net/services/DNS.json";
13 |
14 | public async ValueTask> GetNsServices(string url)
15 | {
16 | var services = await new JsonToolKit>().DownloadAndConverJsonToObjectAsync(url);
17 | return services;
18 | }
19 |
20 | public async ValueTask> GetServicesByNetwork(string network)
21 | {
22 | if (string.IsNullOrEmpty(network))
23 | {
24 | return null;
25 | }
26 | var nsServices = await GetNsServices(dnsServiceUrl);
27 | if (nsServices.Any())
28 | {
29 | List dnsservices = new List();
30 | foreach (var ns in nsServices)
31 | {
32 | List dnsResult = new List();
33 | var result = await new JsonToolKit>().DownloadAndConverJsonToObjectAsync(ns.Url + "/api/dns/services/symbol/"+ network);
34 | if (result.Any())
35 | {
36 | dnsResult.AddRange(result);
37 | dnsservices.Add(new DnsServices { Url = ns.Url, DnsResults = dnsResult });
38 | }
39 | }
40 | return dnsservices;
41 | }
42 | return null;
43 | }
44 |
45 | public async ValueTask> GetServicesByType(string type)
46 | {
47 | if (string.IsNullOrEmpty(type))
48 | {
49 | return null;
50 | }
51 | var nsServices = await GetNsServices(dnsServiceUrl);
52 | if (nsServices.Any())
53 | {
54 | List dnsservices = new List();
55 | foreach (var ns in nsServices)
56 | {
57 | List dnsResult = new List();
58 | var result = await new JsonToolKit>().DownloadAndConverJsonToObjectAsync(ns.Url + "/api/dns/services/service/" + type);
59 | if (result.Any())
60 | {
61 | dnsResult.AddRange(result);
62 | dnsservices.Add(new DnsServices { Url = ns.Url, DnsResults = dnsResult });
63 | }
64 | }
65 | return dnsservices;
66 | }
67 | return null;
68 | }
69 |
70 | public async ValueTask> GetServicesByTypeAndNetwork(string type, string network)
71 | {
72 | if (string.IsNullOrEmpty(type) || string.IsNullOrEmpty(network))
73 | {
74 | return null;
75 | }
76 |
77 | var nsServices = await GetNsServices(dnsServiceUrl);
78 | if (nsServices.Any())
79 | {
80 | List dnsservices = new List();
81 | foreach (var ns in nsServices)
82 | {
83 | List dnsResult = new List();
84 | var result = await new JsonToolKit>().DownloadAndConverJsonToObjectAsync(ns.Url + "/api/dns/services/symbol/"+ network + "/service/" + type);
85 | if (result.Any())
86 | {
87 | dnsResult.AddRange(result);
88 | dnsservices.Add(new DnsServices { Url = ns.Url, DnsResults = dnsResult });
89 | }
90 | }
91 | return dnsservices;
92 | }
93 | return null;
94 | }
95 |
96 | public string GetDnsServiceUrl()
97 | {
98 | return dnsServiceUrl;
99 | }
100 |
101 | }
102 | }
103 |
--------------------------------------------------------------------------------
/src/blazor/dapp-wasm/Blockcore.AtomicSwaps.BlockcoreDns/IBlockcoreDnsService.cs:
--------------------------------------------------------------------------------
1 |
2 | using Blockcore.AtomicSwaps.BlockcoreDns.Models;
3 | using System.Collections.Generic;
4 | using System.Net;
5 | using System.Threading.Tasks;
6 |
7 | namespace Blockcore.AtomicSwaps.BlockcoreDns
8 | {
9 | public interface IBlockcoreDnsService
10 | {
11 | string GetDnsServiceUrl();
12 | ValueTask> GetNsServices(string url);
13 | ValueTask> GetServicesByType(string type);
14 | ValueTask> GetServicesByNetwork(string network);
15 | ValueTask> GetServicesByTypeAndNetwork(string type, string network);
16 | }
17 | }
--------------------------------------------------------------------------------
/src/blazor/dapp-wasm/Blockcore.AtomicSwaps.BlockcoreDns/Models/DnsResult.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 |
7 | namespace Blockcore.AtomicSwaps.BlockcoreDns.Models
8 | {
9 | public class DnsResult
10 | {
11 | public string Domain { get; set; }
12 | public string Symbol { get; set; }
13 | public string Service { get; set; }
14 | public int Ttl { get; set; }
15 | public bool Online { get; set; }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/src/blazor/dapp-wasm/Blockcore.AtomicSwaps.BlockcoreDns/Models/DnsServices.cs:
--------------------------------------------------------------------------------
1 | using Newtonsoft.Json;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Linq;
5 | using System.Text;
6 | using System.Threading.Tasks;
7 |
8 | namespace Blockcore.AtomicSwaps.BlockcoreDns.Models
9 | {
10 | public class DnsServices
11 | {
12 | public string Url { get; set; }
13 | public List DnsResults { get; set; }
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/src/blazor/dapp-wasm/Blockcore.AtomicSwaps.BlockcoreDns/Models/NsResult.cs:
--------------------------------------------------------------------------------
1 | using Newtonsoft.Json;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Linq;
5 | using System.Text;
6 | using System.Threading.Tasks;
7 |
8 | namespace Blockcore.AtomicSwaps.BlockcoreDns.Models
9 | {
10 | public class NsResult
11 | {
12 | [JsonProperty("url")]
13 | public string Url { get; set; }
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/src/blazor/dapp-wasm/Blockcore.AtomicSwaps.BlockcoreDns/ServiceCollectionExtensions.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Extensions.DependencyInjection;
2 |
3 | namespace Blockcore.AtomicSwaps.BlockcoreDns
4 | {
5 | public static class ServiceCollectionExtensions
6 | {
7 | public static void AddBlockcoreDns(this IServiceCollection services)
8 | {
9 | services.AddScoped();
10 | }
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/src/blazor/dapp-wasm/Blockcore.AtomicSwaps.BlockcoreDns/Toolkit/JsonToolKit.cs:
--------------------------------------------------------------------------------
1 | using Newtonsoft.Json;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Diagnostics.CodeAnalysis;
5 | using System.Linq;
6 | using System.Net.Http;
7 | using System.Text;
8 | using System.Threading;
9 | using System.Threading.Tasks;
10 |
11 |
12 | namespace Blockcore.AtomicSwaps.BlockcoreDns.Toolkit
13 | {
14 | public class JsonToolKit where T : class
15 | {
16 | public T ConverJsonToObject(string json)
17 | {
18 | var result = JsonConvert.DeserializeObject(json);
19 | return result;
20 | }
21 |
22 | public async Task DownloadAndConverJsonToObjectAsync(string Url)
23 | {
24 | try
25 | {
26 | using (var httpClient = new HttpClient())
27 | {
28 | Uri uri = new Uri(Url);
29 | var json = await httpClient.GetStringAsync(uri);
30 | var result = JsonConvert.DeserializeObject(json);
31 | return result;
32 | }
33 | }
34 | catch
35 | {
36 | return null;
37 | }
38 |
39 | }
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/src/blazor/dapp-wasm/Blockcore.AtomicSwaps.BlockcoreDns/_Imports.razor:
--------------------------------------------------------------------------------
1 | @using System.Net.Http
2 | @using System.Net.Http.Json
3 | @using Microsoft.AspNetCore.Components.Forms
4 | @using Microsoft.AspNetCore.Components.Routing
5 | @using Microsoft.AspNetCore.Components.Web
6 | @using Microsoft.AspNetCore.Components.Web.Virtualization
7 | @using Microsoft.AspNetCore.Components.WebAssembly.Http
8 | @using Microsoft.JSInterop
9 |
--------------------------------------------------------------------------------
/src/blazor/dapp-wasm/Blockcore.AtomicSwaps.BlockcoreWallet/Blockcore.AtomicSwaps.BlockcoreWallet.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net7.0
5 | enable
6 | nullable
7 | True
8 | True
9 | Blockcore
10 | 1.0.0
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/src/blazor/dapp-wasm/Blockcore.AtomicSwaps.BlockcoreWallet/BlockcoreWallet.razor:
--------------------------------------------------------------------------------
1 | @page "/blockcorewallet"
2 | @using Microsoft.JSInterop
3 | @inject IJSRuntime JSRuntime;
4 |
5 | Blockcore Wallet
6 |
7 | Blockcore Wallet!
8 | @if (!HasBlockcoreWallet)
9 | {
10 | No Blockcore Wallet detected. Please install Blockcore Wallet.
11 | }
12 | else
13 | {
14 |
15 |
16 |
19 |
20 |
21 |
22 |
23 |
24 |
27 |
28 |
29 |
30 |
31 |
32 |
35 |
36 |
37 |
38 |
39 |
40 |
43 |
44 |
45 |
46 |
47 |
48 |
51 |
52 |
53 |
54 |
55 |
56 |
59 |
60 |
61 |
62 |
63 | }
64 |
--------------------------------------------------------------------------------
/src/blazor/dapp-wasm/Blockcore.AtomicSwaps.BlockcoreWallet/BlockcoreWallet.razor.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Threading.Tasks;
3 | using Blockcore.AtomicSwaps.BlockcoreWallet;
4 | using Blockcore.AtomicSwaps.BlockcoreWallet.Exceptions;
5 | using Microsoft.AspNetCore.Components;
6 |
7 |
8 | namespace Blockcore.AtomicSwaps.BlockcoreWallet
9 | {
10 |
11 | public partial class BlockcoreWallet : IDisposable
12 | {
13 | private bool disposedValue;
14 |
15 | [Inject]
16 | public IBlockcoreWalletConnector BlockcoreWalletConnector { get; set; } = default!;
17 | public bool HasBlockcoreWallet { get; set; }
18 | string[] arr = new string[] { };
19 |
20 | public string? SignedMessage { get; set; }
21 | public string? SignedMessageAnyAccount { get; set; }
22 | public string? SignedMessageAnyAccountJson { get; set; }
23 | public string? PaymentRequestResult { get; set; }
24 | public string? DIDSupportedMethodsResult { get; set; }
25 | public string? DIDRequestResult { get; set; }
26 |
27 | protected override async Task OnInitializedAsync()
28 | {
29 | HasBlockcoreWallet = await BlockcoreWalletConnector.HasBlockcoreWallet();
30 | }
31 |
32 | public async Task SignMessageAnyAccount(string value)
33 | {
34 | var result = await BlockcoreWalletConnector.SignMessageAnyAccount(value);
35 | SignedMessageAnyAccount = $"Signed: {result}";
36 | }
37 |
38 | public async Task SignMessageAnyAccountJson(string value)
39 | {
40 | var result = await BlockcoreWalletConnector.SignMessageAnyAccountJson(value);
41 | SignedMessageAnyAccountJson = $"Signed: {result}";
42 | }
43 |
44 | public async Task PaymentRequest(string network, string amount)
45 | {
46 | var result = await BlockcoreWalletConnector.PaymentRequest(network, amount);
47 | PaymentRequestResult = $"{result}";
48 | }
49 |
50 | public async Task DIDSupportedMethods()
51 | {
52 | var result = await BlockcoreWalletConnector.DIDSupportedMethods();
53 | DIDSupportedMethodsResult = $"{result}";
54 | }
55 |
56 | public async Task DIDRequest(string[] methods)
57 | {
58 | var result = await BlockcoreWalletConnector.DIDRequest(methods);
59 | DIDRequestResult = $"{result}";
60 | }
61 |
62 | public async Task SignMessage(string message)
63 | {
64 | try
65 | {
66 | var result = await BlockcoreWalletConnector.SignMessage(message);
67 | SignedMessage = $"Signed: {result}";
68 | }
69 | catch (UserDeniedException)
70 | {
71 | SignedMessage = "User Denied";
72 | }
73 | catch (Exception ex)
74 | {
75 | SignedMessage = $"Exception: {ex}";
76 | }
77 | }
78 |
79 |
80 |
81 |
82 | protected virtual void Dispose(bool disposing)
83 | {
84 | if (!disposedValue)
85 | {
86 | if (disposing)
87 | {
88 | // TODO: dispose managed state (managed objects)
89 | }
90 |
91 | // TODO: free unmanaged resources (unmanaged objects) and override finalizer
92 | // TODO: set large fields to null
93 | disposedValue = true;
94 | }
95 | }
96 |
97 |
98 |
99 | public void Dispose()
100 | {
101 | // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
102 | Dispose(disposing: true);
103 | GC.SuppressFinalize(this);
104 | }
105 | }
106 |
107 |
108 | }
109 |
--------------------------------------------------------------------------------
/src/blazor/dapp-wasm/Blockcore.AtomicSwaps.BlockcoreWallet/BlockcoreWalletConnector.cs:
--------------------------------------------------------------------------------
1 | using Blockcore.AtomicSwaps.BlockcoreWallet.Exceptions;
2 | using Microsoft.JSInterop;
3 | using System;
4 | using System.Numerics;
5 | using System.Reflection.Emit;
6 | using System.Text.Json;
7 | using System.Threading.Tasks;
8 |
9 | namespace Blockcore.AtomicSwaps.BlockcoreWallet
10 | {
11 | // This class provides JavaScript functionality for BlockcoreWallet wrapped
12 | // in a .NET class for easy consumption. The associated JavaScript module is
13 | // loaded on demand when first needed.
14 | //
15 | // This class can be registered as scoped DI service and then injected into Blazor
16 | // components for use.
17 |
18 | public class BlockcoreWalletConnector : IAsyncDisposable, IBlockcoreWalletConnector
19 | {
20 | private readonly Lazy> moduleTask;
21 |
22 | //public static event Func? ConnectEvent;
23 | //public static event Func? DisconnectEvent;
24 |
25 | public BlockcoreWalletConnector(IJSRuntime jsRuntime)
26 | {
27 | moduleTask = new(() => LoadScripts(jsRuntime).AsTask());
28 | }
29 |
30 | public ValueTask LoadScripts(IJSRuntime jsRuntime)
31 | {
32 | return jsRuntime.InvokeAsync("import", "./_content/Blockcore.AtomicSwaps.BlockcoreWallet/blockcoreWallet.js");
33 | }
34 |
35 | public async ValueTask ConnectBlockcoreWallet()
36 | {
37 | var module = await moduleTask.Value;
38 | try
39 | {
40 | await module.InvokeVoidAsync("checkBlockcoreWallet");
41 | }
42 | catch (Exception ex)
43 | {
44 | HandleExceptions(ex);
45 | throw;
46 | }
47 | }
48 |
49 | public async ValueTask HasBlockcoreWallet()
50 | {
51 | var module = await moduleTask.Value;
52 | try
53 | {
54 | return await module.InvokeAsync("hasBlockcoreWallet");
55 | }
56 | catch (Exception ex)
57 | {
58 | HandleExceptions(ex);
59 | throw;
60 | }
61 | }
62 |
63 | public async ValueTask IsSiteConnected()
64 | {
65 | var module = await moduleTask.Value;
66 | try
67 | {
68 | return await module.InvokeAsync("isSiteConnected");
69 | }
70 | catch (Exception ex)
71 | {
72 | HandleExceptions(ex);
73 | throw;
74 | }
75 | }
76 |
77 | public async ValueTask SendCoins(BlockcoreWalletSendFunds data)
78 | {
79 | var input = JsonSerializer.Serialize(data);
80 | var module = await moduleTask.Value;
81 | try
82 | {
83 | var result = await module.InvokeAsync("sendCoins", input);
84 | return JsonSerializer.Deserialize(result);
85 | }
86 | catch (Exception ex)
87 | {
88 | HandleExceptions(ex);
89 | throw;
90 | }
91 | }
92 |
93 | public async ValueTask SwapCoins(BlockcoreWalletSwapCoins data)
94 | {
95 | var input = JsonSerializer.Serialize(data);
96 | var module = await moduleTask.Value;
97 | try
98 | {
99 | var result = await module.InvokeAsync("swapCoins", input);
100 | return JsonSerializer.Deserialize(result);
101 | }
102 | catch (Exception ex)
103 | {
104 | HandleExceptions(ex);
105 | throw;
106 | }
107 | }
108 |
109 | public async ValueTask GetWallet(string? key = null)
110 | {
111 | var module = await moduleTask.Value;
112 | try
113 | {
114 | var result = await module.InvokeAsync("getWallet", key);
115 | return JsonSerializer.Deserialize(result);
116 |
117 | }
118 | catch (Exception ex)
119 | {
120 | HandleExceptions(ex);
121 | throw;
122 | }
123 | }
124 |
125 | public async ValueTask GetSwapKey(string key, string walletId, string accountId, bool includePrivateKey)
126 | {
127 | var module = await moduleTask.Value;
128 | try
129 | {
130 | return await module.InvokeAsync("getSwapKey", key, walletId, accountId, includePrivateKey);
131 | }
132 | catch (Exception ex)
133 | {
134 | HandleExceptions(ex);
135 | throw;
136 | }
137 | }
138 |
139 | public async ValueTask GetSwapSecret(string key, string walletId, string accountId, string message)
140 | {
141 | var module = await moduleTask.Value;
142 | try
143 | {
144 | return await module.InvokeAsync("getSwapSecret", key, walletId, accountId, message);
145 | }
146 | catch (Exception ex)
147 | {
148 | HandleExceptions(ex);
149 | throw;
150 | }
151 | }
152 |
153 | public async ValueTask SignMessageAnyAccount(string value)
154 | {
155 | var module = await moduleTask.Value;
156 | try
157 | {
158 | return await module.InvokeAsync("signMessageAnyAccount", value);
159 |
160 |
161 | }
162 | catch (Exception ex)
163 | {
164 | HandleExceptions(ex);
165 | throw;
166 | }
167 | }
168 |
169 | public async ValueTask SignMessageAnyAccountJson(string value)
170 | {
171 | var module = await moduleTask.Value;
172 | try
173 | {
174 | return await module.InvokeAsync("signMessageAnyAccountJson", value);
175 | }
176 | catch (Exception ex)
177 | {
178 | HandleExceptions(ex);
179 | throw;
180 | }
181 | }
182 |
183 | public async ValueTask PaymentRequest(string network, string amount)
184 | {
185 | var module = await moduleTask.Value;
186 | try
187 | {
188 | return await module.InvokeAsync("paymentRequest", network, amount);
189 | }
190 | catch (Exception ex)
191 | {
192 | HandleExceptions(ex);
193 | throw;
194 | }
195 | }
196 |
197 | public async ValueTask DIDSupportedMethods()
198 | {
199 | var module = await moduleTask.Value;
200 | try
201 | {
202 | return await module.InvokeAsync("didSupportedMethods");
203 | }
204 | catch (Exception ex)
205 | {
206 | HandleExceptions(ex);
207 | throw;
208 | }
209 | }
210 |
211 | public async ValueTask DIDRequest(string[] methods)
212 | {
213 | var module = await moduleTask.Value;
214 | try
215 | {
216 | return await module.InvokeAsync("didRequest", methods);
217 | }
218 | catch (Exception ex)
219 | {
220 | HandleExceptions(ex);
221 | throw;
222 | }
223 | }
224 |
225 | public async ValueTask SignMessage(string msg)
226 | {
227 | var module = await moduleTask.Value;
228 | try
229 | {
230 | return await module.InvokeAsync("signMessage", msg);
231 | }
232 | catch (Exception ex)
233 | {
234 | HandleExceptions(ex);
235 | throw;
236 | }
237 | }
238 |
239 | public async ValueTask DisposeAsync()
240 | {
241 | if (moduleTask.IsValueCreated)
242 | {
243 | var module = await moduleTask.Value;
244 | await module.DisposeAsync();
245 | }
246 | }
247 |
248 | private void HandleExceptions(Exception ex)
249 | {
250 | switch (ex.Message)
251 | {
252 | case "NoBlockcoreWallet":
253 | throw new NoBlockcoreWalletException();
254 | case "UserDenied":
255 | throw new UserDeniedException();
256 | }
257 | }
258 |
259 | }
260 | }
261 |
--------------------------------------------------------------------------------
/src/blazor/dapp-wasm/Blockcore.AtomicSwaps.BlockcoreWallet/BlockcoreWalletModels.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.JSInterop;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Numerics;
5 | using System.Threading.Tasks;
6 | #pragma warning disable CS8618
7 |
8 | namespace Blockcore.AtomicSwaps.BlockcoreWallet
9 | {
10 | public class BlockcoreWalletSendFundsRecipients
11 | {
12 | public string address { get; set; } = null!;
13 | public long amount { get; set; }
14 | public string hint { get; set; } = string.Empty;
15 | }
16 | public class BlockcoreWalletSendFunds
17 | {
18 | public List recipients { get; set; } = new();
19 |
20 | public string data { get; set; }
21 | public string feeRate { get; set; }
22 | public string network { get; set; }
23 | }
24 |
25 | public class BlockcoreWalletSendFundsOut
26 | {
27 | public string transactionId { get; set; }
28 | public string transactionHex { get; set; }
29 | }
30 |
31 | public class BlockcoreWalletSwapCoins
32 | {
33 | public string walletId { get; set; }
34 | public string accountId { get; set; }
35 | public string swapTrxHex { get; set; }
36 | public string trxToSignHex { get; set; }
37 | public string redeemScriptHex { get; set; }
38 | public string secretHashHex { get; set; }
39 | public string network { get; set; }
40 | public string message { get; set; }
41 | }
42 |
43 | public class BlockcoreWalletSwapCoinsOut
44 | {
45 | public string privateKey { get; set; }
46 | public string transactionId { get; set; }
47 | public string transactionHex { get; set; }
48 |
49 | }
50 |
51 | public class BlockcoreWalletMessageOut
52 | {
53 | public string key { get; set; }
54 | public ContentData response { get; set; }
55 |
56 | public class Account
57 | {
58 | public string icon { get; set; }
59 | public string name { get; set; }
60 | public string id { get; set; }
61 | public int network { get; set; }
62 | public string networkType { get; set; }
63 | public int purpose { get; set; }
64 | public int purposeAddress { get; set; }
65 | public string type { get; set; }
66 | public History history { get; set; }
67 | public State state { get; set; }
68 | public NetworkDefinition networkDefinition { get; set; }
69 | }
70 |
71 | public class Bip32
72 | {
73 | public int @public { get; set; }
74 | public int @private { get; set; }
75 | }
76 |
77 | public class Change
78 | {
79 | public string address { get; set; }
80 | public int index { get; set; }
81 | }
82 |
83 | public class Confirmation
84 | {
85 | public int low { get; set; }
86 | public int high { get; set; }
87 | public int count { get; set; }
88 | }
89 |
90 | public class History
91 | {
92 | public long balance { get; set; }
93 | public List history { get; set; }
94 | public int unconfirmed { get; set; }
95 | public List unspent { get; set; }
96 | }
97 |
98 | public class HistoryItem
99 | {
100 | public int blockIndex { get; set; }
101 | public string calculatedAddress { get; set; }
102 | public long calculatedValue { get; set; }
103 | public string entryType { get; set; }
104 | public int fee { get; set; }
105 | public bool finalized { get; set; }
106 | public bool isCoinbase { get; set; }
107 | public bool isCoinstake { get; set; }
108 | public int timestamp { get; set; }
109 | public string transactionHash { get; set; }
110 | }
111 |
112 | public class NetworkDefinition
113 | {
114 | public string id { get; set; }
115 | public string name { get; set; }
116 | public string symbol { get; set; }
117 | public int network { get; set; }
118 | public int purpose { get; set; }
119 | public string messagePrefix { get; set; }
120 | public string bech32 { get; set; }
121 | public Bip32 bip32 { get; set; }
122 | public int pubKeyHash { get; set; }
123 | public int scriptHash { get; set; }
124 | public int wif { get; set; }
125 | public int minimumFeeRate { get; set; }
126 | public int maximumFeeRate { get; set; }
127 | public bool testnet { get; set; }
128 | public bool isProofOfStake { get; set; }
129 | public bool smartContractSupport { get; set; }
130 | public string type { get; set; }
131 | public int? purposeAddress { get; set; }
132 | }
133 |
134 | public class Param
135 | {
136 | public object key { get; set; }
137 | }
138 |
139 | public class Peg
140 | {
141 | public string type { get; set; }
142 | public string address { get; set; }
143 | }
144 |
145 | public class Receive
146 | {
147 | public string address { get; set; }
148 | public int index { get; set; }
149 | }
150 |
151 |
152 | public class ContentData
153 | {
154 | public Wallet wallet { get; set; }
155 | public List accounts { get; set; }
156 | }
157 |
158 | public class State
159 | {
160 | public int balance { get; set; }
161 | public List change { get; set; }
162 | public bool completedScan { get; set; }
163 | public string id { get; set; }
164 | public DateTime lastScan { get; set; }
165 | public List receive { get; set; }
166 | }
167 |
168 | public class Unspent
169 | {
170 | public string address { get; set; }
171 | public long balance { get; set; }
172 | public int index { get; set; }
173 | public string transactionHash { get; set; }
174 | public bool unconfirmed { get; set; }
175 | }
176 |
177 | public class Wallet
178 | {
179 | public string id { get; set; }
180 | public string name { get; set; }
181 | public string key { get; set; }
182 | }
183 |
184 | }
185 | }
--------------------------------------------------------------------------------
/src/blazor/dapp-wasm/Blockcore.AtomicSwaps.BlockcoreWallet/Exceptions/NoBlockcoreWalletException.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 |
7 | namespace Blockcore.AtomicSwaps.BlockcoreWallet.Exceptions
8 | {
9 | public class NoBlockcoreWalletException : ApplicationException
10 | {
11 | public NoBlockcoreWalletException() : base("BlockcoreWallet is not installed.")
12 | {
13 |
14 | }
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/src/blazor/dapp-wasm/Blockcore.AtomicSwaps.BlockcoreWallet/Exceptions/UserDeniedException.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 |
7 | namespace Blockcore.AtomicSwaps.BlockcoreWallet.Exceptions
8 | {
9 | public class UserDeniedException : ApplicationException
10 | {
11 | public UserDeniedException() : base("User denied access to BlockcoreWallet.")
12 | {
13 |
14 | }
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/src/blazor/dapp-wasm/Blockcore.AtomicSwaps.BlockcoreWallet/IBlockcoreWalletConnector.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.JSInterop;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Numerics;
5 | using System.Threading.Tasks;
6 |
7 | namespace Blockcore.AtomicSwaps.BlockcoreWallet
8 | {
9 | public interface IBlockcoreWalletConnector
10 | {
11 |
12 | ValueTask ConnectBlockcoreWallet();
13 | ValueTask DisposeAsync();
14 | ValueTask HasBlockcoreWallet();
15 | ValueTask IsSiteConnected();
16 | ValueTask SignMessageAnyAccount(string value);
17 | ValueTask GetWallet(string? key = null);
18 | ValueTask GetSwapKey(string key, string walletId, string accountId, bool includePrivateKey);
19 | ValueTask GetSwapSecret(string key, string walletId, string accountId, string message);
20 | ValueTask SignMessageAnyAccountJson(string value);
21 | ValueTask PaymentRequest(string network, string amount);
22 | ValueTask DIDSupportedMethods();
23 | ValueTask DIDRequest(string[] methods);
24 | ValueTask SignMessage(string msg);
25 | ValueTask SendCoins(BlockcoreWalletSendFunds data);
26 | ValueTask SwapCoins(BlockcoreWalletSwapCoins data);
27 | }
28 | }
--------------------------------------------------------------------------------
/src/blazor/dapp-wasm/Blockcore.AtomicSwaps.BlockcoreWallet/ServiceCollectionExtensions.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Extensions.DependencyInjection;
2 | using Microsoft.JSInterop;
3 |
4 | namespace Blockcore.AtomicSwaps.BlockcoreWallet
5 | {
6 | public static class ServiceCollectionExtensions
7 | {
8 | public static void AddBlockcoreWallet(this IServiceCollection services)
9 | {
10 | services.AddScoped(sp => new BlockcoreWalletConnector(sp.GetRequiredService()));
11 | }
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/src/blazor/dapp-wasm/Blockcore.AtomicSwaps.BlockcoreWallet/_Imports.razor:
--------------------------------------------------------------------------------
1 | @using System.Net.Http
2 | @using System.Net.Http.Json
3 | @using Microsoft.AspNetCore.Components.Forms
4 | @using Microsoft.AspNetCore.Components.Routing
5 | @using Microsoft.AspNetCore.Components.Web
6 | @using Microsoft.AspNetCore.Components.Web.Virtualization
7 | @using Microsoft.AspNetCore.Components.WebAssembly.Http
8 | @using Microsoft.JSInterop
9 |
--------------------------------------------------------------------------------
/src/blazor/dapp-wasm/Blockcore.AtomicSwaps.BlockcoreWallet/wwwroot/blockcoreWallet.js:
--------------------------------------------------------------------------------
1 | export async function hasBlockcoreWallet() {
2 | return (globalThis.blockcore != undefined);
3 | }
4 |
5 | export async function signMessageAnyAccount(value) {
6 | const provider = globalThis.blockcore;
7 |
8 | const result = await provider.request({
9 | method: 'signMessage',
10 | params: [{ message: value }],
11 | });
12 | console.log('Result:', result);
13 | return JSON.stringify(result);
14 |
15 | //var key = result.key;
16 | //var signature = result.signature;
17 | //var network = result.network;
18 | //var verify = bitcoinMessage.verify(value, result.key, result.signature);
19 | }
20 |
21 | export async function sendCoins(input) {
22 | const provider = globalThis.blockcore;
23 |
24 | var data = JSON.parse(input)
25 | const result = await provider.request({
26 | method: 'transaction.send',
27 | params: [data],
28 | });
29 | console.log('Result:', result);
30 | return JSON.stringify(result);
31 | }
32 |
33 | export async function swapCoins(input) {
34 | const provider = globalThis.blockcore;
35 |
36 | var data = JSON.parse(input)
37 | const result = await provider.request({
38 | method: 'atomicswaps.send',
39 | params: [data],
40 | });
41 | console.log('Result:', result);
42 | return JSON.stringify(result.response);
43 | }
44 |
45 | export async function getWallet(key) {
46 | const provider = globalThis.blockcore;
47 |
48 | const result = await provider.request({
49 | method: 'wallets',
50 | params: [{ key: key }],
51 | });
52 | console.log('Result:', result);
53 | return JSON.stringify(result);
54 | }
55 |
56 | export async function getSwapKey(key, walletId, accountId, includePrivateKey) {
57 | const provider = globalThis.blockcore;
58 |
59 | const result = await provider.request({
60 | method: 'atomicswaps.key',
61 | params: [{ key: key, walletId: walletId, accountId: accountId, includePrivateKey: includePrivateKey }],
62 | });
63 | console.log('Result:', result);
64 | return JSON.stringify(result);
65 | }
66 |
67 | export async function getSwapSecret(key, walletId, accountId, message) {
68 | const provider = globalThis.blockcore;
69 |
70 | const result = await provider.request({
71 | method: 'atomicswaps.secret',
72 | params: [{ key: key, walletId: walletId, accountId: accountId, message: message }],
73 | });
74 | console.log('Result:', result);
75 | return JSON.stringify(result);
76 | }
77 |
78 | export async function signMessageAnyAccountJson(value) {
79 | const message = JSON.parse(value);
80 |
81 | const provider = globalThis.blockcore;
82 |
83 | const result = await provider.request({
84 | method: 'signMessage',
85 | params: [{ message: message }],
86 | });
87 |
88 | console.log('Result:', result);
89 | return JSON.stringify(result);
90 |
91 | //this.signedJsonKey = result.key;
92 | //this.signedJsonSignature = result.signature;
93 | //this.signedJsonNetwork = result.network;
94 | //const preparedMessage = JSON.stringify(message);
95 | //this.signedJsonValidSignature = bitcoinMessage.verify(preparedMessage, result.key, result.signature);
96 | }
97 |
98 | export async function paymentRequest(network, amount) {
99 | try {
100 | const provider = globalThis.blockcore;
101 |
102 | var result = await provider.request({
103 | method: 'payment',
104 | params: [
105 | {
106 | network: network.toLowerCase(),
107 | amount: amount,
108 | address: 'Ccoquhaae7u6ASqQ5BiYueASz8EavUXrKn',
109 | label: 'Your Local Info',
110 | message: 'Invoice Number 5',
111 | data: 'MzExMzUzNDIzNDY',
112 | id: '4324',
113 | },
114 | ],
115 | });
116 |
117 | console.log('Result:', result);
118 | return JSON.stringify(result);
119 | } catch (err) {
120 | console.error(err);
121 | }
122 | }
123 |
124 | async function request(method, params) {
125 | if (!params) {
126 | params = [];
127 | }
128 | const provider = globalThis.blockcore;
129 | const result = await provider.request({
130 | method: method,
131 | params: params,
132 | });
133 | console.log('Result:', result);
134 |
135 | return result;
136 | }
137 |
138 | export async function didSupportedMethods() {
139 | const result = await request('did.supportedMethods');
140 | return JSON.stringify(result.response);
141 | }
142 |
143 | export async function didRequest(methods) {
144 | const result = await request('did.request', [
145 | {
146 | challenge: 'fc0949c4-fd9c-4825-b18d-79348e358156',
147 | methods: methods,
148 | reason: 'Sample app need access to any of your DIDs.',
149 | },
150 | ]);
151 |
152 | return JSON.stringify(result.response);
153 | }
154 |
155 | export async function signMessage(msg) {
156 | const provider = globalThis.blockcore;
157 | let signature;
158 | try {
159 | signature = await provider.request({ method: "signMessage", params: [{ scheme: "schnorr", message: msg }] });
160 | return JSON.stringify(signature);
161 | }
162 | catch (error) {
163 | console.error("Error: " + error.message);
164 | // User denied account access...
165 | throw "UserDenied";
166 | }
167 | }
168 |
169 |
170 |
171 |
172 |
173 |
--------------------------------------------------------------------------------
/src/blazor/dapp-wasm/Blockcore.MetaMask/Blockcore.MetaMask.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net7.0
5 | enable
6 | nullable
7 | True
8 | True
9 | Blockcore
10 | 1.0.0
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/src/blazor/dapp-wasm/Blockcore.MetaMask/Enums/Chain.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 |
7 | namespace Blockcore.AtomicSwaps.MetaMask.Enums
8 | {
9 | ///
10 | /// https://docs.metamask.io/guide/ethereum-provider.html#chain-ids
11 | ///
12 | public enum Chain
13 | {
14 | Mainnet = 1,
15 | Ropsten = 3,
16 | Rinkeby = 4,
17 | Goerli = 5,
18 | Kovan = 42,
19 | BinanceSmartChain = 56,
20 | BinanceTestnet = 97
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/src/blazor/dapp-wasm/Blockcore.MetaMask/Exceptions/NoMetaMaskException.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 |
7 | namespace Blockcore.AtomicSwaps.MetaMask.Exceptions
8 | {
9 | public class NoMetaMaskException : ApplicationException
10 | {
11 | public NoMetaMaskException() : base("MetaMask is not installed.")
12 | {
13 |
14 | }
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/src/blazor/dapp-wasm/Blockcore.MetaMask/Exceptions/UserDeniedException.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 |
7 | namespace Blockcore.AtomicSwaps.MetaMask.Exceptions
8 | {
9 | public class UserDeniedException : ApplicationException
10 | {
11 | public UserDeniedException() : base("User denied access to MetaMask.")
12 | {
13 |
14 | }
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/src/blazor/dapp-wasm/Blockcore.MetaMask/Extensions/HexExtensions.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Numerics;
5 | using System.Text;
6 | using System.Threading.Tasks;
7 |
8 | namespace Blockcore.AtomicSwaps.MetaMask.Extensions
9 | {
10 | public static class HexExtensions
11 | {
12 | public static long HexToInt(this string hexString)
13 | {
14 | if (hexString.StartsWith("0x"))
15 | hexString = hexString[2..];
16 |
17 | return long.Parse(hexString, System.Globalization.NumberStyles.HexNumber);
18 | }
19 | public static BigInteger HexToBigInteger(this string hexString)
20 | {
21 | if (hexString.StartsWith("0x"))
22 | hexString = hexString[2..];
23 |
24 | return BigInteger.Parse(hexString, System.Globalization.NumberStyles.HexNumber);
25 | }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/src/blazor/dapp-wasm/Blockcore.MetaMask/Extensions/SendTransactionExtensions.cs:
--------------------------------------------------------------------------------
1 | using Nethereum.ABI.FunctionEncoding;
2 | using Nethereum.ABI.Model;
3 | using Nethereum.JsonRpc.Client;
4 | using Nethereum.RPC.Eth.DTOs;
5 | using Nethereum.RPC.Eth.Transactions;
6 | using System.Numerics;
7 | using System.Threading.Tasks;
8 |
9 | namespace Blockcore.AtomicSwaps.MetaMask
10 | {
11 | public static class SendTransactionExtensions
12 | {
13 | private static async Task CallFunction(IMetaMaskService metaMask, string funcName, string contractAddress, BigInteger valueInWei, Parameter[] paramaters, params object[] values)
14 | {
15 | FunctionABI function = new FunctionABI(funcName, false);
16 |
17 | function.InputParameters = paramaters;
18 | var functionCallEncoder = new FunctionCallEncoder();
19 |
20 | var data = functionCallEncoder.EncodeRequest(function.Sha3Signature, paramaters,
21 | values);
22 |
23 | data = data[2..];
24 | var result = await metaMask.SendTransaction(contractAddress, valueInWei, data);
25 |
26 | return result;
27 | }
28 |
29 | private static async Task GetTransactionReceipt(string txHash, IClient client, int interval = 1000)
30 | {
31 | EthGetTransactionReceipt getTransactionReceipt = new EthGetTransactionReceipt(client);
32 | TransactionReceipt? receipt = null;
33 | while (receipt == null)
34 | {
35 | receipt = await getTransactionReceipt.SendRequestAsync(txHash);
36 | await Task.Delay(interval);
37 | }
38 | return receipt;
39 | }
40 |
41 | ///
42 | /// Send transaction to smart contract and wait for receipt
43 | ///
44 | /// Web3 Client
45 | /// Function To Call
46 | /// Smart Contract Address
47 | /// Value In Wei
48 | /// Function Parameters
49 | /// Function Values
50 | /// Receipt Of Transaction
51 | public static async Task SendTransactionAndWaitForReceipt
52 | (
53 | this IMetaMaskService metaMask,
54 | IClient client,
55 | string funcName,
56 | string contractAddress,
57 | BigInteger value,
58 | Parameter[] inputParams,
59 | params object[] values
60 | )
61 | {
62 | var txHash = await CallFunction(metaMask, funcName, contractAddress, value, inputParams, values);
63 | var receipt = await GetTransactionReceipt(txHash, client);
64 | return receipt;
65 | }
66 |
67 | ///
68 | /// Send transaction to smart contract and wait for receipt
69 | ///
70 | /// Web3 Client
71 | /// Function To Call
72 | /// Smart Contract Address
73 | /// Value In Wei
74 | /// Function Parameters
75 | /// Function Values
76 | /// The interval at which we check for a transaction receipt
77 | /// Receipt Of Transaction
78 | public static async Task SendTransactionAndWaitForReceipt
79 | (
80 | this IMetaMaskService metaMask,
81 | IClient client,
82 | string funcName,
83 | string contractAddress,
84 | BigInteger value,
85 | Parameter[] inputParams,
86 | int interval = 1000,
87 | params object[] values
88 | )
89 | {
90 | var txHash = await CallFunction(metaMask, funcName, contractAddress, value, inputParams, values);
91 | return await GetTransactionReceipt(txHash, client, interval);
92 | }
93 | }
94 | }
--------------------------------------------------------------------------------
/src/blazor/dapp-wasm/Blockcore.MetaMask/IMetaMaskService.cs:
--------------------------------------------------------------------------------
1 | using Blockcore.AtomicSwaps.MetaMask.Enums;
2 | using Blockcore.AtomicSwaps.MetaMask.Extensions;
3 | using Microsoft.JSInterop;
4 | using System;
5 | using System.Numerics;
6 | using System.Threading.Tasks;
7 |
8 | namespace Blockcore.AtomicSwaps.MetaMask
9 | {
10 | public interface IMetaMaskService
11 | {
12 | public static event Func? AccountChangedEvent;
13 | public static event Func<(long, Chain), Task>? ChainChangedEvent;
14 |
15 | ValueTask ConnectMetaMask();
16 | ValueTask DisposeAsync();
17 | ValueTask GenericRpc(string method, params dynamic[]? args);
18 | Task GetBalance(string address, string block = "latest");
19 | ValueTask GetSelectedAddress();
20 | ValueTask<(long chainId, Chain chain)> GetSelectedChain();
21 | ValueTask GetTransactionCount();
22 | ValueTask HasMetaMask();
23 | ValueTask IsSiteConnected();
24 | ValueTask ListenToEvents();
25 | ValueTask LoadScripts(IJSRuntime jsRuntime);
26 | Task RequestAccounts();
27 | ValueTask SendTransaction(string to, BigInteger weiValue, string? data = null);
28 | ValueTask SignTypedData(string label, string value);
29 | ValueTask SignTypedDataV4(string typedData);
30 |
31 | [JSInvokable()]
32 | static async Task OnAccountsChanged(string selectedAccount)
33 | {
34 | if (AccountChangedEvent != null)
35 | {
36 | await AccountChangedEvent.Invoke(selectedAccount);
37 | }
38 | }
39 |
40 | [JSInvokable()]
41 | static async Task OnChainChanged(string chainhex)
42 | {
43 | if (ChainChangedEvent != null)
44 | {
45 | await ChainChangedEvent.Invoke(ChainHexToChainResponse(chainhex));
46 | }
47 | }
48 |
49 | protected static (long chainId, Chain chain) ChainHexToChainResponse(string chainHex)
50 | {
51 | long chainId = chainHex.HexToInt();
52 | return (chainId, (Chain)chainId);
53 | }
54 | }
55 | }
--------------------------------------------------------------------------------
/src/blazor/dapp-wasm/Blockcore.MetaMask/MetaMaskService.cs:
--------------------------------------------------------------------------------
1 | using Blockcore.AtomicSwaps.MetaMask.Enums;
2 | using Blockcore.AtomicSwaps.MetaMask.Exceptions;
3 | using Blockcore.AtomicSwaps.MetaMask.Extensions;
4 | using Microsoft.JSInterop;
5 | using System;
6 | using System.Numerics;
7 | using System.Text.Json;
8 | using System.Threading.Tasks;
9 |
10 | namespace Blockcore.AtomicSwaps.MetaMask
11 | {
12 | // This class provides JavaScript functionality for MetaMask wrapped
13 | // in a .NET class for easy consumption. The associated JavaScript module is
14 | // loaded on demand when first needed.
15 | //
16 | // This class can be registered as scoped DI service and then injected into Blazor
17 | // components for use.
18 |
19 | public class MetaMaskService : IAsyncDisposable, IMetaMaskService
20 | {
21 | private readonly Lazy> moduleTask;
22 |
23 | //public static event Func? ConnectEvent;
24 | //public static event Func? DisconnectEvent;
25 |
26 | public MetaMaskService(IJSRuntime jsRuntime)
27 | {
28 | moduleTask = new(() => LoadScripts(jsRuntime).AsTask());
29 | }
30 |
31 | public ValueTask LoadScripts(IJSRuntime jsRuntime)
32 | {
33 | //await jsRuntime.InvokeAsync("import", "https://cdn.ethers.io/lib/ethers-5.1.0.umd.min.js");
34 | return jsRuntime.InvokeAsync("import", "./_content/Blockcore.MetaMask/metaMask.js");
35 | }
36 |
37 | public async ValueTask ConnectMetaMask()
38 | {
39 | var module = await moduleTask.Value;
40 | try
41 | {
42 | await module.InvokeVoidAsync("checkMetaMask");
43 | }
44 | catch (Exception ex)
45 | {
46 | HandleExceptions(ex);
47 | throw;
48 | }
49 | }
50 |
51 | public async ValueTask HasMetaMask()
52 | {
53 | var module = await moduleTask.Value;
54 | try
55 | {
56 | return await module.InvokeAsync("hasMetaMask");
57 | }
58 | catch (Exception ex)
59 | {
60 | HandleExceptions(ex);
61 | throw;
62 | }
63 | }
64 |
65 | public async ValueTask IsSiteConnected()
66 | {
67 | var module = await moduleTask.Value;
68 | try
69 | {
70 | return await module.InvokeAsync("isSiteConnected");
71 | }
72 | catch (Exception ex)
73 | {
74 | HandleExceptions(ex);
75 | throw;
76 | }
77 | }
78 |
79 | public async ValueTask ListenToEvents()
80 | {
81 | var module = await moduleTask.Value;
82 | try
83 | {
84 | await module.InvokeVoidAsync("listenToChangeEvents");
85 | }
86 | catch (Exception ex)
87 | {
88 | HandleExceptions(ex);
89 | throw;
90 | }
91 | }
92 |
93 | public async ValueTask GetSelectedAddress()
94 | {
95 | var module = await moduleTask.Value;
96 | try
97 | {
98 | return await module.InvokeAsync("getSelectedAddress", null);
99 | }
100 | catch (Exception ex)
101 | {
102 | HandleExceptions(ex);
103 | throw;
104 | }
105 | }
106 |
107 | public async ValueTask<(long chainId, Chain chain)> GetSelectedChain()
108 | {
109 | var module = await moduleTask.Value;
110 | try
111 | {
112 | string chainHex = await module.InvokeAsync("getSelectedChain", null);
113 | return IMetaMaskService.ChainHexToChainResponse(chainHex);
114 | }
115 | catch (Exception ex)
116 | {
117 | HandleExceptions(ex);
118 | throw;
119 | }
120 | }
121 |
122 | public async ValueTask GetTransactionCount()
123 | {
124 | var module = await moduleTask.Value;
125 | try
126 | {
127 | var result = await module.InvokeAsync("getTransactionCount");
128 | var resultString = result.GetString();
129 | if (resultString != null)
130 | {
131 | long intValue = resultString.HexToInt();
132 | return intValue;
133 | }
134 | return 0;
135 | }
136 | catch (Exception ex)
137 | {
138 | HandleExceptions(ex);
139 | throw;
140 | }
141 | }
142 |
143 | public async ValueTask SignTypedData(string label, string value)
144 | {
145 | var module = await moduleTask.Value;
146 | try
147 | {
148 | return await module.InvokeAsync("signTypedData", label, value);
149 | }
150 | catch (Exception ex)
151 | {
152 | HandleExceptions(ex);
153 | throw;
154 | }
155 | }
156 |
157 | public async ValueTask SignTypedDataV4(string typedData)
158 | {
159 | var module = await moduleTask.Value;
160 | try
161 | {
162 | return await module.InvokeAsync("signTypedDataV4", typedData);
163 | }
164 | catch (Exception ex)
165 | {
166 | HandleExceptions(ex);
167 | throw;
168 | }
169 | }
170 |
171 | public async ValueTask SendTransaction(string to, BigInteger weiValue, string? data = null)
172 | {
173 | var module = await moduleTask.Value;
174 | try
175 | {
176 | string hexValue = "0x" + weiValue.ToString("X");
177 | return await module.InvokeAsync("sendTransaction", to, hexValue, data);
178 | }
179 | catch (Exception ex)
180 | {
181 | HandleExceptions(ex);
182 | throw;
183 | }
184 | }
185 |
186 | public async ValueTask GenericRpc(string method, params dynamic?[]? args)
187 | {
188 | var module = await moduleTask.Value;
189 | try
190 | {
191 | return await module.InvokeAsync("genericRpc", method, args);
192 | }
193 | catch (Exception ex)
194 | {
195 | HandleExceptions(ex);
196 | throw;
197 | }
198 | }
199 |
200 | public async Task RequestAccounts()
201 | {
202 | var result = await GenericRpc("eth_requestAccounts");
203 |
204 | return result.ToString();
205 | }
206 |
207 | public async Task GetBalance(string address, string block = "latest")
208 | {
209 | var result = await GenericRpc("eth_getBalance", address, block);
210 |
211 | string hex = result.ToString();
212 |
213 | return hex.HexToBigInteger();
214 | }
215 |
216 | //[JSInvokable()]
217 | //public static async Task OnConnect()
218 | //{
219 | // Console.WriteLine("connected");
220 | // if (ConnectEvent != null)
221 | // {
222 | // await ConnectEvent.Invoke();
223 | // }
224 | //}
225 |
226 | //[JSInvokable()]
227 | //public static async Task OnDisconnect()
228 | //{
229 | // Console.WriteLine("disconnected");
230 | // if (DisconnectEvent != null)
231 | // {
232 | // await DisconnectEvent.Invoke();
233 | // }
234 | //}
235 |
236 | public async ValueTask DisposeAsync()
237 | {
238 | if (moduleTask.IsValueCreated)
239 | {
240 | var module = await moduleTask.Value;
241 | await module.DisposeAsync();
242 | }
243 | }
244 |
245 | private void HandleExceptions(Exception ex)
246 | {
247 | switch (ex.Message)
248 | {
249 | case "NoMetaMask":
250 | throw new NoMetaMaskException();
251 | case "UserDenied":
252 | throw new UserDeniedException();
253 | }
254 | }
255 | }
256 | }
257 |
--------------------------------------------------------------------------------
/src/blazor/dapp-wasm/Blockcore.MetaMask/Metamask.razor:
--------------------------------------------------------------------------------
1 | @page "/metamask"
2 | @using Nethereum.Signer
3 |
4 | MetaMask
5 |
6 | MetaMask
7 |
8 | @if (!HasMetaMask)
9 | {
10 | No MetaMask detected. Please install MetaMask.
11 | }
12 | else if (string.IsNullOrEmpty(SelectedAddress))
13 | {
14 |
15 |
16 |
17 | }
18 | else
19 | {
20 |
21 |
22 | @SelectedAddress
23 |
24 |
25 |
26 |
27 | @SelectedChain
28 |
29 |
30 |
31 |
32 | @TransactionCount
33 |
34 |
35 |
36 |
37 | @SignedData
38 |
39 |
40 |
41 |
42 | @SignedDataV4
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 | @RpcResult
52 |
53 |
54 | Kovan testnet only:
55 | Interact with a smart contract. These two example smart contracts are only deployed to the Kovan testnet. Make sure to select Kovan in MetaMask
56 |
57 | @if (Chain == Blockcore.AtomicSwaps.MetaMask.Enums.Chain.Kovan)
58 | {
59 |
60 |
61 |
62 |
63 |
64 | @FunctionResult
65 |
66 | }
67 | else
68 | {
69 | Please switch to Kovan testnet!
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/src/blazor/dapp-wasm/Blockcore.MetaMask/Metamask.razor.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Numerics;
4 | using System.Threading.Tasks;
5 | using Blockcore.AtomicSwaps.MetaMask;
6 | using Blockcore.AtomicSwaps.MetaMask.Enums;
7 | using Blockcore.AtomicSwaps.MetaMask.Exceptions;
8 | using Blockcore.AtomicSwaps.MetaMask.Models;
9 | using Microsoft.AspNetCore.Components;
10 | using Nethereum.ABI.FunctionEncoding;
11 | using Nethereum.ABI.Model;
12 | using Nethereum.Web3;
13 |
14 | namespace Blockcore.MetaMask
15 | {
16 |
17 | public partial class Metamask : IDisposable
18 | {
19 | [Inject]
20 | public IMetaMaskService MetaMaskService { get; set; } = default!;
21 |
22 | public bool HasMetaMask { get; set; }
23 | public string? SelectedAddress { get; set; }
24 | public string? SelectedChain { get; set; }
25 | public string? TransactionCount { get; set; }
26 | public string? SignedData { get; set; }
27 | public string? SignedDataV4 { get; set; }
28 | public string? FunctionResult { get; set; }
29 | public string? RpcResult { get; set; }
30 | public Chain? Chain { get; set; }
31 |
32 | protected override async Task OnInitializedAsync()
33 | {
34 | //Subscribe to events
35 | IMetaMaskService.AccountChangedEvent += MetaMaskService_AccountChangedEvent;
36 | IMetaMaskService.ChainChangedEvent += MetaMaskService_ChainChangedEvent;
37 |
38 | HasMetaMask = await MetaMaskService.HasMetaMask();
39 | if (HasMetaMask)
40 | await MetaMaskService.ListenToEvents();
41 |
42 | bool isSiteConnected = await MetaMaskService.IsSiteConnected();
43 | if (isSiteConnected)
44 | {
45 | await GetSelectedAddress();
46 | await GetSelectedNetwork();
47 | }
48 |
49 | }
50 |
51 | private async Task MetaMaskService_ChainChangedEvent((long, Chain) arg)
52 | {
53 | await GetSelectedNetwork();
54 | StateHasChanged();
55 | }
56 |
57 | private async Task MetaMaskService_AccountChangedEvent(string arg)
58 | {
59 | await GetSelectedAddress();
60 | StateHasChanged();
61 | }
62 |
63 | public async Task ConnectMetaMask()
64 | {
65 | await MetaMaskService.ConnectMetaMask();
66 | await GetSelectedAddress();
67 | }
68 |
69 | public async Task GetSelectedAddress()
70 | {
71 | SelectedAddress = await MetaMaskService.GetSelectedAddress();
72 | Console.WriteLine($"Address: {SelectedAddress}");
73 | }
74 |
75 | public async Task GetSelectedNetwork()
76 | {
77 | var chainInfo = await MetaMaskService.GetSelectedChain();
78 | Chain = chainInfo.chain;
79 |
80 | SelectedChain = $"ChainID: {chainInfo.chainId}, Name: {chainInfo.chain.ToString()}";
81 | Console.WriteLine($"ChainID: {chainInfo.chainId}");
82 | }
83 |
84 | public async Task GetTransactionCount()
85 | {
86 | var transactionCount = await MetaMaskService.GetTransactionCount();
87 | TransactionCount = $"Transaction count: {transactionCount}";
88 | }
89 |
90 | public async Task SignData(string label, string value)
91 | {
92 | try
93 | {
94 | var result = await MetaMaskService.SignTypedData("test label", "test value");
95 | SignedData = $"Signed: {result}";
96 | }
97 | catch (UserDeniedException)
98 | {
99 | SignedData = "User Denied";
100 | }
101 | catch (Exception ex)
102 | {
103 | SignedData = $"Exception: {ex}";
104 | }
105 | }
106 |
107 | public async Task SignDataV4()
108 | {
109 | try
110 | {
111 | var chainInfo = await MetaMaskService.GetSelectedChain();
112 |
113 | var data = new TypedDataPayload
114 | {
115 | Domain = new AtomicSwaps.MetaMask.Models.Domain
116 | {
117 | Name = "AAA",
118 | Version = "1",
119 | ChainId = chainInfo.chainId
120 | },
121 | Types = new Dictionary
122 | {
123 | ["EIP712Domain"] = new[]
124 | {
125 | new TypeMemberValue { Name = "name", Type = "string" },
126 | new TypeMemberValue { Name = "version", Type = "string" },
127 | new TypeMemberValue { Name = "chainId", Type = "uint256" }
128 | },
129 | ["Message"] = new[]
130 | {
131 | new TypeMemberValue { Name = "contents", Type = "string" }
132 | }
133 | },
134 | PrimaryType = "Message",
135 | Message = new AtomicSwaps.MetaMask.Models.Message
136 | {
137 | contents = "Blockcore"
138 | }
139 | };
140 |
141 | var result = await MetaMaskService.SignTypedDataV4(data.ToJson());
142 |
143 | SignedDataV4 = $"Signed: {result}";
144 | }
145 | catch (UserDeniedException)
146 | {
147 | SignedDataV4 = "User Denied";
148 | }
149 | catch (Exception ex)
150 | {
151 | SignedDataV4 = $"Exception: {ex}";
152 | }
153 | }
154 |
155 | public async Task CallSmartContractFunctionExample1()
156 | {
157 | try
158 | {
159 | string address = "0x21253c6f5E16a56031dc8d8AF0bb372bc4A4DfDA";
160 | BigInteger weiValue = 0;
161 | string data = GetEncodedFunctionCall();
162 |
163 | data = data[2..]; //Remove the 0x from the generated string
164 | var result = await MetaMaskService.SendTransaction(address, weiValue, data);
165 | FunctionResult = $"TX Hash: {result}";
166 | }
167 | catch (UserDeniedException)
168 | {
169 | FunctionResult = "User Denied";
170 | }
171 | catch (Exception ex)
172 | {
173 | FunctionResult = $"Exception: {ex}";
174 | }
175 | }
176 |
177 | public async Task CallSmartContractFunctionExample2()
178 | {
179 | try
180 | {
181 | string address = "0x722BcdA7BD1a0f8C1c9b7c0eefabE36c1f0fBF2a";
182 | BigInteger weiValue = 1000000000000000;
183 | string data = GetEncodedFunctionCallExample2();
184 |
185 | data = data[2..]; //Remove the 0x from the generated string
186 | var result = await MetaMaskService.SendTransaction(address, weiValue, data);
187 | FunctionResult = $"TX Hash: {result}";
188 | }
189 | catch (UserDeniedException)
190 | {
191 | FunctionResult = "User Denied";
192 | }
193 | catch (Exception ex)
194 | {
195 | FunctionResult = $"Exception: {ex}";
196 | }
197 | }
198 |
199 | private string GetEncodedFunctionCall()
200 | {
201 | //This example uses Nethereum.ABI to create the ABI encoded string to call a smart contract function
202 |
203 | //Smart contract has a function called "share"
204 | FunctionABI function = new FunctionABI("share", false);
205 |
206 | //With 4 inputs
207 | var inputsParameters = new[] {
208 | new Parameter("address", "receiver"),
209 | new Parameter("string", "appId"),
210 | new Parameter("string", "shareType"),
211 | new Parameter("string", "data")
212 | };
213 | function.InputParameters = inputsParameters;
214 |
215 | var functionCallEncoder = new FunctionCallEncoder();
216 |
217 | var data = functionCallEncoder.EncodeRequest(function.Sha3Signature, inputsParameters,
218 | "0x92B143F46C3F8B4242bA85F800579cdF73882e98",
219 | "Blockcore.AtomicSwaps.MetaMask",
220 | "Sample",
221 | DateTime.UtcNow.ToString());
222 | return data;
223 | }
224 |
225 | private string GetEncodedFunctionCallExample2()
226 | {
227 | //This example uses Nethereum.ABI to create the ABI encoded string to call a smart contract function
228 |
229 | //Smart contract has a function called "share"
230 | FunctionABI function = new FunctionABI("setColor", false);
231 |
232 | //With 4 inputs
233 | var inputsParameters = new[] {
234 | new Parameter("string", "color")
235 | };
236 | function.InputParameters = inputsParameters;
237 |
238 | var functionCallEncoder = new FunctionCallEncoder();
239 |
240 | var data = functionCallEncoder.EncodeRequest(function.Sha3Signature, inputsParameters, new object[] { "green" });
241 |
242 | return data;
243 | }
244 |
245 |
246 | public async Task GenericRpc()
247 | {
248 | var result = await MetaMaskService.RequestAccounts();
249 | RpcResult = $"RPC result: {result}";
250 | }
251 |
252 | public async Task GetBalance()
253 | {
254 | var address = await MetaMaskService.GetSelectedAddress();
255 | var result = await MetaMaskService.GetBalance(address);
256 | var balance = Web3.Convert.FromWei(result);
257 | RpcResult = $"Balance result: {Math.Round(balance, 4)} ETH";
258 | }
259 |
260 | public void Dispose()
261 | {
262 | IMetaMaskService.AccountChangedEvent -= MetaMaskService_AccountChangedEvent;
263 | IMetaMaskService.ChainChangedEvent -= MetaMaskService_ChainChangedEvent;
264 | }
265 | }
266 |
267 | }
268 |
--------------------------------------------------------------------------------
/src/blazor/dapp-wasm/Blockcore.MetaMask/Models/TypedDataPayload.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Numerics;
3 | using Newtonsoft.Json;
4 | using Newtonsoft.Json.Serialization;
5 |
6 | namespace Blockcore.AtomicSwaps.MetaMask.Models
7 | {
8 | public record struct Message(string contents);
9 |
10 |
11 | public class TypedDataPayload
12 | {
13 | public Dictionary Types { get; set; } = new();
14 | public string? PrimaryType { get; set; }
15 | public Domain? Domain { get; set; }
16 | public T? Message { get; set; }
17 |
18 |
19 | public string ToJson()
20 | {
21 | var serializerSettings = new JsonSerializerSettings
22 | {
23 | ContractResolver = new CamelCasePropertyNamesContractResolver()
24 | {
25 | NamingStrategy = new CamelCaseNamingStrategy()
26 | {
27 | ProcessDictionaryKeys = false
28 | }
29 | },
30 |
31 | DefaultValueHandling = DefaultValueHandling.Ignore
32 | };
33 | return JsonConvert.SerializeObject(this, serializerSettings);
34 | }
35 | }
36 |
37 | public class Domain
38 | {
39 | public string? Name { get; set; }
40 | public string? Version { get; set; }
41 | public BigInteger? ChainId { get; set; }
42 | }
43 |
44 | public class TypeMemberValue
45 | {
46 | public string? Name { get; set; }
47 | public string? Type { get; set; }
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/src/blazor/dapp-wasm/Blockcore.MetaMask/ServiceCollectionExtensions.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Extensions.DependencyInjection;
2 | using Microsoft.JSInterop;
3 |
4 | namespace Blockcore.AtomicSwaps.MetaMask
5 | {
6 | public static class ServiceCollectionExtensions
7 | {
8 | public static void AddMetaMask(this IServiceCollection services)
9 | {
10 | services.AddScoped(sp => new MetaMaskService(sp.GetRequiredService()));
11 | }
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/src/blazor/dapp-wasm/Blockcore.MetaMask/_Imports.razor:
--------------------------------------------------------------------------------
1 | @using System.Net.Http
2 | @using System.Net.Http.Json
3 | @using Microsoft.AspNetCore.Components.Forms
4 | @using Microsoft.AspNetCore.Components.Routing
5 | @using Microsoft.AspNetCore.Components.Web
6 | @using Microsoft.AspNetCore.Components.Web.Virtualization
7 | @using Microsoft.AspNetCore.Components.WebAssembly.Http
8 | @using Microsoft.JSInterop
9 |
--------------------------------------------------------------------------------
/src/blazor/dapp-wasm/Blockcore.MetaMask/wwwroot/metaMask.js:
--------------------------------------------------------------------------------
1 | // This is a JavaScript module that is loaded on demand. It can export any number of
2 | // functions, and may import other JavaScript modules if required.
3 |
4 | export async function checkMetaMask() {
5 | // Modern dapp browsers...
6 | if (window.ethereum) {
7 | if (ethereum.selectedAddress === null || ethereum.selectedAddress === undefined) {
8 | try {
9 | // Request account access if needed
10 | await requestAccounts();
11 | } catch (error) {
12 | // User denied account access...
13 | throw "UserDenied"
14 | }
15 | }
16 | else {
17 | console.log("Selected:" + ethereum.selectedAddress);
18 | }
19 | }
20 | // Non-dapp browsers...
21 | else {
22 | throw "NoMetaMask"
23 | }
24 | }
25 |
26 | export async function requestAccounts() {
27 | console.log('reqAccount');
28 | var result = await ethereum.request({
29 | method: 'eth_requestAccounts',
30 | });
31 | return result;
32 | }
33 |
34 | export function hasMetaMask() {
35 | return (window.ethereum != undefined);
36 | }
37 |
38 | export function isSiteConnected() {
39 | return (window.ethereum != undefined && (ethereum.selectedAddress != undefined || ethereum.selectedAddress != null));
40 | }
41 |
42 | export async function getSelectedAddress() {
43 | await checkMetaMask();
44 |
45 | return ethereum.selectedAddress;
46 | }
47 |
48 | export async function listenToChangeEvents() {
49 | if (hasMetaMask()) {
50 | //ethereum.on("connect", function () {
51 | // DotNet.invokeMethodAsync('Blockcore.AtomicSwaps.MetaMask', 'OnConnect');
52 | //});
53 |
54 | //ethereum.on("disconnect", function () {
55 | // DotNet.invokeMethodAsync('Blockcore.AtomicSwaps.MetaMask', 'OnDisconnect');
56 | //});
57 |
58 | ethereum.on("accountsChanged", function (accounts) {
59 | DotNet.invokeMethodAsync('Blockcore.AtomicSwaps.MetaMask', 'OnAccountsChanged', accounts[0]);
60 | });
61 |
62 | ethereum.on("chainChanged", function (chainId) {
63 | DotNet.invokeMethodAsync('Blockcore.AtomicSwaps.MetaMask', 'OnChainChanged', chainId);
64 | });
65 | }
66 | }
67 |
68 | export async function getSelectedChain() {
69 | await checkMetaMask();
70 |
71 | var result = await ethereum.request({
72 | method: 'eth_chainId'
73 | });
74 | return result;
75 | }
76 |
77 | export async function getTransactionCount() {
78 | await checkMetaMask();
79 |
80 | var result = await ethereum.request({
81 | method: 'eth_getTransactionCount',
82 | params:
83 | [
84 | ethereum.selectedAddress,
85 | 'latest'
86 | ]
87 |
88 | });
89 | return result;
90 | }
91 |
92 | export async function signTypedData(label, value) {
93 | await checkMetaMask();
94 |
95 | const msgParams = [
96 | {
97 | type: 'string', // Valid solidity type
98 | name: label,
99 | value: value
100 | }
101 | ]
102 |
103 | try {
104 | var result = await ethereum.request({
105 | method: 'eth_signTypedData',
106 | params:
107 | [
108 | msgParams,
109 | ethereum.selectedAddress
110 | ]
111 | });
112 |
113 | return result;
114 | } catch (error) {
115 | // User denied account access...
116 | throw "UserDenied"
117 | }
118 | }
119 |
120 | export async function signTypedDataV4(typedData) {
121 | await checkMetaMask();
122 |
123 | try {
124 | var result = await ethereum.request({
125 | method: 'eth_signTypedData_v4',
126 | params:
127 | [
128 | ethereum.selectedAddress,
129 | typedData
130 | ],
131 | from: ethereum.selectedAddress
132 | });
133 |
134 | return result;
135 | } catch (error) {
136 | // User denied account access...
137 | throw "UserDenied"
138 | }
139 | }
140 |
141 | export async function sendTransaction(to, value, data) {
142 | await checkMetaMask();
143 |
144 | const transactionParameters = {
145 | to: to,
146 | from: ethereum.selectedAddress, // must match user's active address.
147 | value: value,
148 | data: data
149 | };
150 |
151 | try {
152 | var result = await ethereum.request({
153 | method: 'eth_sendTransaction',
154 | params: [transactionParameters]
155 | });
156 |
157 | return result;
158 | } catch (error) {
159 | if (error.code == 4001) {
160 | throw "UserDenied"
161 | }
162 | throw error;
163 | }
164 | }
165 |
166 | export async function genericRpc(method, params) {
167 | await checkMetaMask();
168 |
169 | var result = await ethereum.request({
170 | method: method,
171 | params: params
172 | });
173 |
174 | return result;
175 | }
--------------------------------------------------------------------------------
/src/blazor/dapp-wasm/dapp-wasm.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 17
4 | VisualStudioVersion = 17.4.33110.190
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "dapp-wasm", "dapp-wasm\dapp-wasm.csproj", "{791D7A5E-0A39-49E0-BABA-E11A6581FCFF}"
7 | EndProject
8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Blockcore.MetaMask", "Blockcore.MetaMask\Blockcore.MetaMask.csproj", "{E959D45D-6931-4977-89A7-60BF2FDDE18D}"
9 | EndProject
10 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Blockcore.AtomicSwaps.BlockcoreDns", "Blockcore.AtomicSwaps.BlockcoreDns\Blockcore.AtomicSwaps.BlockcoreDns.csproj", "{6E5116F7-7A42-45C8-A389-6BD7DBBEF052}"
11 | EndProject
12 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Blockcore.AtomicSwaps.BlockcoreWallet", "Blockcore.AtomicSwaps.BlockcoreWallet\Blockcore.AtomicSwaps.BlockcoreWallet.csproj", "{602033E1-57C3-458F-BC2C-B0B1E88A91E7}"
13 | EndProject
14 | Global
15 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
16 | Debug|Any CPU = Debug|Any CPU
17 | Release|Any CPU = Release|Any CPU
18 | EndGlobalSection
19 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
20 | {791D7A5E-0A39-49E0-BABA-E11A6581FCFF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
21 | {791D7A5E-0A39-49E0-BABA-E11A6581FCFF}.Debug|Any CPU.Build.0 = Debug|Any CPU
22 | {791D7A5E-0A39-49E0-BABA-E11A6581FCFF}.Release|Any CPU.ActiveCfg = Release|Any CPU
23 | {791D7A5E-0A39-49E0-BABA-E11A6581FCFF}.Release|Any CPU.Build.0 = Release|Any CPU
24 | {E959D45D-6931-4977-89A7-60BF2FDDE18D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
25 | {E959D45D-6931-4977-89A7-60BF2FDDE18D}.Debug|Any CPU.Build.0 = Debug|Any CPU
26 | {E959D45D-6931-4977-89A7-60BF2FDDE18D}.Release|Any CPU.ActiveCfg = Release|Any CPU
27 | {E959D45D-6931-4977-89A7-60BF2FDDE18D}.Release|Any CPU.Build.0 = Release|Any CPU
28 | {6E5116F7-7A42-45C8-A389-6BD7DBBEF052}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
29 | {6E5116F7-7A42-45C8-A389-6BD7DBBEF052}.Debug|Any CPU.Build.0 = Debug|Any CPU
30 | {6E5116F7-7A42-45C8-A389-6BD7DBBEF052}.Release|Any CPU.ActiveCfg = Release|Any CPU
31 | {6E5116F7-7A42-45C8-A389-6BD7DBBEF052}.Release|Any CPU.Build.0 = Release|Any CPU
32 | {602033E1-57C3-458F-BC2C-B0B1E88A91E7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
33 | {602033E1-57C3-458F-BC2C-B0B1E88A91E7}.Debug|Any CPU.Build.0 = Debug|Any CPU
34 | {602033E1-57C3-458F-BC2C-B0B1E88A91E7}.Release|Any CPU.ActiveCfg = Release|Any CPU
35 | {602033E1-57C3-458F-BC2C-B0B1E88A91E7}.Release|Any CPU.Build.0 = Release|Any CPU
36 | EndGlobalSection
37 | GlobalSection(SolutionProperties) = preSolution
38 | HideSolutionNode = FALSE
39 | EndGlobalSection
40 | GlobalSection(ExtensibilityGlobals) = postSolution
41 | SolutionGuid = {A87AE351-CD8C-43F7-A8F8-F88181F0352E}
42 | EndGlobalSection
43 | EndGlobal
44 |
--------------------------------------------------------------------------------
/src/blazor/dapp-wasm/dapp-wasm/App.razor:
--------------------------------------------------------------------------------
1 | @using Blockcore.MetaMask
2 |
6 |
7 |
8 |
9 |
10 |
11 | Not found
12 |
13 | Sorry, there's nothing at this address.
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/src/blazor/dapp-wasm/dapp-wasm/Data/BlockData.cs:
--------------------------------------------------------------------------------
1 | namespace Data;
2 |
3 | public class BlockData
4 | {
5 | public string blockHash { get; set; }
6 |
7 | public int blockIndex { get; set; }
8 |
9 |
10 | }
11 |
--------------------------------------------------------------------------------
/src/blazor/dapp-wasm/dapp-wasm/Data/BtcDecimalJsonConverter.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Globalization;
3 | using Newtonsoft.Json;
4 |
5 | namespace Blockcore.Controllers.Converters
6 | {
7 | ///
8 | /// Converts a decimal value to a string with the minimum number of decimals used by bitcoin (8).
9 | ///
10 | public class BtcDecimalJsonConverter : JsonConverter
11 | {
12 | private const int MinDecimals = 8;
13 |
14 | ///
15 | /// Method for writing a string formatted decimal to Json that truncates at decimal points.
16 | /// A instance.
17 | /// The value to be written.
18 | /// A instance.
19 | public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
20 | {
21 | decimal btcDecimal = (decimal)value;
22 | string result = btcDecimal.ToString(CultureInfo.InvariantCulture);
23 | if (!result.Contains('.') || result.Split('.')[1].Length < MinDecimals)
24 | {
25 | result = btcDecimal.ToString("0." + new string('0', MinDecimals), CultureInfo.InvariantCulture);
26 | }
27 |
28 | writer.WriteRawValue(result);
29 | }
30 |
31 | ///
32 | /// A method for reading a string formatted decimal in Json that was truncated at decimals.
33 | ///
34 | /// Not implemented.
35 | public override object ReadJson(JsonReader reader, Type objectType, object existingValue,
36 | JsonSerializer serializer)
37 | {
38 | throw new NotImplementedException();
39 | }
40 |
41 | public override bool CanRead => false;
42 |
43 | public override bool CanConvert(Type objectType) => objectType == typeof(decimal);
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/src/blazor/dapp-wasm/dapp-wasm/Data/ToStringJsonConverter.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Newtonsoft.Json;
3 |
4 | namespace Blockcore.Controllers.Converters
5 | {
6 | public class ToStringJsonConverter : JsonConverter
7 | {
8 | public override bool CanConvert(Type objectType)
9 | {
10 | return true;
11 | }
12 |
13 | public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
14 | {
15 | writer.WriteValue(value.ToString());
16 | }
17 |
18 | public override bool CanRead
19 | {
20 | get { return false; }
21 | }
22 |
23 | public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
24 | {
25 | throw new NotImplementedException();
26 | }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/blazor/dapp-wasm/dapp-wasm/Data/TransactionModel.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Linq;
3 | using Blockcore.Consensus.Chain;
4 | using Blockcore.Consensus.ScriptInfo;
5 | using Blockcore.Consensus.TransactionInfo;
6 | using Blockcore.Controllers.Converters;
7 | using Blockcore.Networks;
8 | using NBitcoin;
9 | using NBitcoin.DataEncoders;
10 | using Newtonsoft.Json;
11 |
12 | namespace Blockcore.Controllers.Models
13 | {
14 | ///
15 | /// A class representing a transaction.
16 | ///
17 | public abstract class TransactionModel
18 | {
19 | public TransactionModel(Network network = null)
20 | {
21 | }
22 |
23 | ///
24 | /// Creates a containing the hash of the given transaction.
25 | ///
26 | /// A valid
27 | public TransactionModel(Transaction trx)
28 | {
29 | this.Hex = trx?.ToHex();
30 | }
31 |
32 | /// The transaction in hexadecimal format.
33 | [JsonProperty(Order = 0, PropertyName = "hex", NullValueHandling = NullValueHandling.Ignore)]
34 | public string Hex { get; set; }
35 |
36 | public override string ToString()
37 | {
38 | return this.Hex;
39 | }
40 | }
41 |
42 | ///
43 | /// Creates a concise transaction model containing the hashed transaction.
44 | ///
45 | [JsonConverter(typeof(ToStringJsonConverter))]
46 | public class TransactionBriefModel : TransactionModel
47 | {
48 | public TransactionBriefModel()
49 | {
50 | }
51 |
52 | public TransactionBriefModel(Transaction trx) : base(trx)
53 | {
54 | }
55 | }
56 |
57 | ///
58 | /// Creates a more robust transaction model.
59 | ///
60 | public class TransactionVerboseModel : TransactionModel
61 | {
62 | public TransactionVerboseModel()
63 | {
64 | }
65 |
66 | ///
67 | /// Initializes a new instance of the class.
68 | ///
69 | /// The transaction.
70 | /// The network the transaction occurred on.
71 | /// A of the block that contains the transaction.
72 | /// A of the current tip.
73 | public TransactionVerboseModel(Transaction trx, Network network, ChainedHeader block = null, ChainedHeader tip = null) : base(trx)
74 | {
75 | if (trx != null)
76 | {
77 | this.TxId = trx.GetHash().ToString();
78 | this.Hash = trx.HasWitness ? trx.GetWitHash().ToString() : trx.GetHash().ToString();
79 | this.Size = trx.GetSerializedSize();
80 | this.VSize = trx.HasWitness ? trx.GetVirtualSize(network.Consensus.Options.WitnessScaleFactor) : trx.GetSerializedSize();
81 | this.Version = trx.Version;
82 | this.LockTime = trx.LockTime;
83 |
84 | // size = (weight + WITNESS_SCALE_FACTOR - 1) / WITNESS_SCALE_FACTOR;
85 | // hence, weight = size * WITNESS_SCALE_FACTOR - (WITNESS_SCALE_FACTOR - 1) (only subtract if has witness).
86 | this.Weight = this.VSize * network.Consensus.Options.WitnessScaleFactor - (network.Consensus.Options.WitnessScaleFactor - 1);
87 |
88 | this.VIn = trx.Inputs.Select(txin => new Vin(txin.PrevOut, txin.Sequence, txin.ScriptSig)).ToList();
89 |
90 | int n = 0;
91 | this.VOut = trx.Outputs.Select(txout => new Vout(n++, txout, network)).ToList();
92 |
93 | if (block != null)
94 | {
95 | this.BlockHash = block.HashBlock.ToString();
96 | this.Time = this.BlockTime = Utils.DateTimeToUnixTime(block.Header.BlockTime);
97 | if (tip != null)
98 | this.Confirmations = tip.Height - block.Height + 1;
99 | }
100 | }
101 | }
102 |
103 | /// The transaction id.
104 | [JsonProperty(Order = 1, PropertyName = "txid")]
105 | public string TxId { get; set; }
106 |
107 | /// The transaction hash (differs from txid for witness transactions).
108 | [JsonProperty(Order = 2, PropertyName = "hash")]
109 | public string Hash { get; set; }
110 |
111 | /// The transaction version number (typically 1).
112 | [JsonProperty(Order = 3, PropertyName = "version")]
113 | public uint Version { get; set; }
114 |
115 | /// The serialized transaction size.
116 | [JsonProperty(Order = 4, PropertyName = "size")]
117 | public int Size { get; set; }
118 |
119 | /// The virtual transaction size (differs from size for witness transactions).
120 | [JsonProperty(Order = 5, PropertyName = "vsize")]
121 | public int VSize { get; set; }
122 |
123 | /// The transaction's weight (between vsize*4-3 and vsize*4).
124 | [JsonProperty(Order = 6, PropertyName = "weight", DefaultValueHandling = DefaultValueHandling.Ignore)]
125 | public int Weight { get; set; }
126 |
127 | /// If nonzero, block height or timestamp when transaction is final.
128 | [JsonProperty(Order = 7, PropertyName = "locktime")]
129 | public uint LockTime { get; set; }
130 |
131 | /// A list of inputs.
132 | [JsonProperty(Order = 8, PropertyName = "vin")]
133 | public List VIn { get; set; }
134 |
135 | /// A list of outputs.
136 | [JsonProperty(Order = 9, PropertyName = "vout")]
137 | public List VOut { get; set; }
138 |
139 | /// The hash of the block containing this transaction.
140 | [JsonProperty(Order = 10, PropertyName = "blockhash", DefaultValueHandling = DefaultValueHandling.Ignore)]
141 | public string BlockHash { get; set; }
142 |
143 | /// The number of confirmations of the transaction.
144 | [JsonProperty(Order = 11, PropertyName = "confirmations", DefaultValueHandling = DefaultValueHandling.Ignore)]
145 | public int? Confirmations { get; set; }
146 |
147 | /// The time the transaction was added to a block.
148 | [JsonProperty(Order = 12, PropertyName = "time", DefaultValueHandling = DefaultValueHandling.Ignore)]
149 | public uint? Time { get; set; }
150 |
151 | /// The time the block was confirmed.
152 | [JsonProperty(Order = 13, PropertyName = "blocktime", DefaultValueHandling = DefaultValueHandling.Ignore)]
153 | public uint? BlockTime { get; set; }
154 | }
155 |
156 | ///
157 | /// A class describing a transaction input.
158 | ///
159 | public class Vin
160 | {
161 | public Vin()
162 | {
163 | }
164 |
165 | ///
166 | /// Initializes a instance.
167 | ///
168 | /// The previous output being used as an input.
169 | /// The transaction's sequence number.
170 | /// The scriptSig
171 | public Vin(OutPoint prevOut, Sequence sequence, Consensus.ScriptInfo.Script scriptSig)
172 | {
173 | if (prevOut.Hash == uint256.Zero)
174 | {
175 | this.Coinbase = Encoders.Hex.EncodeData(scriptSig.ToBytes());
176 | }
177 | else
178 | {
179 | this.TxId = prevOut.Hash.ToString();
180 | this.VOut = prevOut.N;
181 | this.ScriptSig = new Script(scriptSig);
182 | }
183 |
184 | this.Sequence = (uint)sequence;
185 | }
186 |
187 | /// The scriptsig if this was a coinbase transaction.
188 | [JsonProperty(Order = 0, PropertyName = "coinbase", DefaultValueHandling = DefaultValueHandling.Ignore)]
189 | public string Coinbase { get; set; }
190 |
191 | /// The transaction ID.
192 | [JsonProperty(Order = 1, PropertyName = "txid", DefaultValueHandling = DefaultValueHandling.Ignore)]
193 | public string TxId { get; set; }
194 |
195 | /// The index of the output.
196 | [JsonProperty(Order = 2, PropertyName = "vout", DefaultValueHandling = DefaultValueHandling.Ignore)]
197 | public uint? VOut { get; set; }
198 |
199 | /// The transaction's scriptsig.
200 | [JsonProperty(Order = 3, PropertyName = "scriptSig", DefaultValueHandling = DefaultValueHandling.Ignore)]
201 | public Script ScriptSig { get; set; }
202 |
203 | /// The transaction's sequence number.
204 | [JsonProperty(Order = 4, PropertyName = "sequence")]
205 | public uint Sequence { get; set; }
206 | }
207 |
208 | ///
209 | /// A class describing a transaction output.
210 | ///
211 | public class Vout
212 | {
213 | public Vout()
214 | {
215 | }
216 |
217 | ///
218 | /// Initializes an instance of the class.
219 | ///
220 | /// The index of the output.
221 | /// A
222 | /// The network where the transaction occured.
223 | public Vout(int n, TxOut txout, Network network)
224 | {
225 | this.N = n;
226 | this.Value = txout.Value.ToDecimal(MoneyUnit.BTC);
227 | this.ScriptPubKey = new ScriptPubKey(txout.ScriptPubKey, network);
228 | }
229 |
230 | /// The value of the output.
231 | [JsonConverter(typeof(BtcDecimalJsonConverter))]
232 | [JsonProperty(Order = 0, PropertyName = "value")]
233 | public decimal Value { get; set; }
234 |
235 | /// The index of the output.
236 | [JsonProperty(Order = 1, PropertyName = "n")]
237 | public int N { get; set; }
238 |
239 | /// The output's scriptpubkey.
240 | [JsonProperty(Order = 2, PropertyName = "scriptPubKey")]
241 | public ScriptPubKey ScriptPubKey { get; set; }
242 | }
243 |
244 | ///
245 | /// A class describing a transaction script.
246 | ///
247 | public class Script
248 | {
249 | public Script()
250 | {
251 | }
252 |
253 | ///
254 | /// Initializes a transaction , which contains the assembly and a hexadecimal representation of the script.
255 | ///
256 | /// A .
257 | public Script(Consensus.ScriptInfo.Script script)
258 | {
259 | this.Asm = script.ToString();
260 | this.Hex = Encoders.Hex.EncodeData(script.ToBytes());
261 | }
262 |
263 | /// The script's assembly.
264 | [JsonProperty(Order = 0, PropertyName = "asm")]
265 | public string Asm { get; set; }
266 |
267 | /// A hexadecimal representation of the script.
268 | [JsonProperty(Order = 1, PropertyName = "hex")]
269 | public string Hex { get; set; }
270 | }
271 |
272 | ///
273 | /// A class describing a ScriptPubKey.
274 | ///
275 | public class ScriptPubKey : Script
276 | {
277 | public ScriptPubKey()
278 | {
279 | }
280 |
281 | ///
282 | /// Initializes an instance of the class.
283 | ///
284 | /// The script.
285 | /// The network where the transaction was conducted.
286 | public ScriptPubKey(Consensus.ScriptInfo.Script script, Network network) : base(script)
287 | {
288 | var destinations = new List { script.GetDestination(network) };
289 | this.Type = this.GetScriptType(script.FindTemplate(network));
290 |
291 | if (destinations[0] == null)
292 | {
293 | destinations = script.GetDestinationPublicKeys(network).Select(p => p.Hash).ToList();
294 | }
295 |
296 | if (destinations.Count == 1)
297 | {
298 | this.ReqSigs = 1;
299 | this.Addresses = new List { destinations[0].GetAddress(network).ToString() };
300 | }
301 | else if (destinations.Count > 1)
302 | {
303 | PayToMultiSigTemplateParameters multi = PayToMultiSigTemplate.Instance.ExtractScriptPubKeyParameters(script);
304 | this.ReqSigs = multi.SignatureCount;
305 | this.Addresses = multi.PubKeys.Select(m => m.GetAddress(network).ToString()).ToList();
306 | }
307 | }
308 |
309 | /// The number of required sigs.
310 | [JsonProperty(Order = 2, PropertyName = "reqSigs", DefaultValueHandling = DefaultValueHandling.Ignore)]
311 | public int? ReqSigs { get; set; }
312 |
313 | /// The type of script.
314 | [JsonProperty(Order = 3, PropertyName = "type", DefaultValueHandling = DefaultValueHandling.Ignore)]
315 | public string Type { get; set; }
316 |
317 | /// A list of output addresses.
318 | [JsonProperty(Order = 4, PropertyName = "addresses", DefaultValueHandling = DefaultValueHandling.Ignore)]
319 | public List Addresses { get; set; }
320 |
321 | ///
322 | /// A method that returns a script type description.
323 | ///
324 | /// A used for the script.
325 | /// A string describin the script type.
326 | protected string GetScriptType(ScriptTemplate template)
327 | {
328 | if (template == null)
329 | return "nonstandard";
330 | switch (template.Type)
331 | {
332 | case TxOutType.TX_PUBKEY:
333 | return "pubkey";
334 |
335 | case TxOutType.TX_PUBKEYHASH:
336 | return "pubkeyhash";
337 |
338 | case TxOutType.TX_SCRIPTHASH:
339 | return "scripthash";
340 |
341 | case TxOutType.TX_MULTISIG:
342 | return "multisig";
343 |
344 | case TxOutType.TX_NULL_DATA:
345 | return "nulldata";
346 | }
347 |
348 | return "nonstandard";
349 | }
350 | }
351 | }
352 |
--------------------------------------------------------------------------------
/src/blazor/dapp-wasm/dapp-wasm/Networks/BitcoinMain.cs:
--------------------------------------------------------------------------------
1 | using Blockcore.Consensus;
2 | using Blockcore.Consensus.ScriptInfo;
3 | using Blockcore.Consensus.TransactionInfo;
4 | using NBitcoin;
5 | using NBitcoin.BitcoinCore;
6 | using NBitcoin.DataEncoders;
7 | using NBitcoin.Protocol;
8 |
9 | namespace Blockcore.Networks.Bitcoin
10 | {
11 | public class BitcoinMain : Network
12 | {
13 | public BitcoinMain()
14 | {
15 | this.Name = "Main";
16 | this.AdditionalNames = new List { "Mainnet" };
17 | this.NetworkType = NetworkType.Mainnet;
18 |
19 | // The message start string is designed to be unlikely to occur in normal data.
20 | // The characters are rarely used upper ASCII, not valid as UTF-8, and produce
21 | // a large 4-byte int at any alignment.
22 | this.Magic = 0xD9B4BEF9;
23 | this.DefaultPort = 8333;
24 | this.DefaultMaxOutboundConnections = 8;
25 | this.DefaultMaxInboundConnections = 117;
26 | this.DefaultRPCPort = 8332;
27 | this.DefaultAPIPort = 37220;
28 | this.MinTxFee = 1000;
29 | this.MaxTxFee = Money.Coins(0.1m).Satoshi;
30 | this.FallbackFee = 20000;
31 | this.MinRelayTxFee = 1000;
32 | this.CoinTicker = "BTC";
33 | this.DefaultBanTimeSeconds = 60 * 60 * 24; // 24 Hours
34 |
35 | var consensusFactory = new ConsensusFactory();
36 |
37 |
38 |
39 | consensusFactory.Protocol = new ConsensusProtocol()
40 | {
41 | ProtocolVersion = ProtocolVersion.FEEFILTER_VERSION,
42 | MinProtocolVersion = ProtocolVersion.SENDHEADERS_VERSION,
43 | };
44 |
45 | this.Consensus = new Consensus.Consensus(
46 | consensusFactory: consensusFactory,
47 | consensusOptions: new ConsensusOptions(), // Default - set to Bitcoin params.
48 | coinType: 0,
49 | hashGenesisBlock: null,
50 | subsidyHalvingInterval: 210000,
51 | majorityEnforceBlockUpgrade: 750,
52 | majorityRejectBlockOutdated: 950,
53 | majorityWindow: 1000,
54 | buriedDeployments: null,
55 | bip9Deployments: null,
56 | bip34Hash: null,
57 | minerConfirmationWindow: 2016, // nPowTargetTimespan / nPowTargetSpacing
58 | maxReorgLength: 0,
59 | defaultAssumeValid: null, // 629000
60 | maxMoney: 21000000 * Money.COIN,
61 | coinbaseMaturity: 100,
62 | premineHeight: 0,
63 | premineReward: Money.Zero,
64 | proofOfWorkReward: Money.Coins(50),
65 | targetTimespan: TimeSpan.FromSeconds(14 * 24 * 60 * 60), // two weeks
66 | targetSpacing: TimeSpan.FromSeconds(10 * 60),
67 | powAllowMinDifficultyBlocks: false,
68 | posNoRetargeting: false,
69 | powNoRetargeting: false,
70 | powLimit: null,
71 | minimumChainWork: null,
72 | isProofOfStake: false,
73 | lastPowBlock: default(int),
74 | proofOfStakeLimit: null,
75 | proofOfStakeLimitV2: null,
76 | proofOfStakeReward: Money.Zero,
77 | proofOfStakeTimestampMask: 0
78 | );
79 |
80 | this.Base58Prefixes = new byte[12][];
81 | this.Base58Prefixes[(int)Base58Type.PUBKEY_ADDRESS] = new byte[] { (0) };
82 | this.Base58Prefixes[(int)Base58Type.SCRIPT_ADDRESS] = new byte[] { (5) };
83 | this.Base58Prefixes[(int)Base58Type.SECRET_KEY] = new byte[] { (128) };
84 | this.Base58Prefixes[(int)Base58Type.ENCRYPTED_SECRET_KEY_NO_EC] = new byte[] { 0x01, 0x42 };
85 | this.Base58Prefixes[(int)Base58Type.ENCRYPTED_SECRET_KEY_EC] = new byte[] { 0x01, 0x43 };
86 | this.Base58Prefixes[(int)Base58Type.EXT_PUBLIC_KEY] = new byte[] { (0x04), (0x88), (0xB2), (0x1E) };
87 | this.Base58Prefixes[(int)Base58Type.EXT_SECRET_KEY] = new byte[] { (0x04), (0x88), (0xAD), (0xE4) };
88 | this.Base58Prefixes[(int)Base58Type.PASSPHRASE_CODE] = new byte[] { 0x2C, 0xE9, 0xB3, 0xE1, 0xFF, 0x39, 0xE2 };
89 | this.Base58Prefixes[(int)Base58Type.CONFIRMATION_CODE] = new byte[] { 0x64, 0x3B, 0xF6, 0xA8, 0x9A };
90 | this.Base58Prefixes[(int)Base58Type.ASSET_ID] = new byte[] { 23 };
91 |
92 | var encoder = new Bech32Encoder("bc");
93 | this.Bech32Encoders = new Bech32Encoder[2];
94 | this.Bech32Encoders[(int)Bech32Type.WITNESS_PUBKEY_ADDRESS] = encoder;
95 | this.Bech32Encoders[(int)Bech32Type.WITNESS_SCRIPT_ADDRESS] = encoder;
96 |
97 | this.StandardScriptsRegistry = new BitcoinStandardScriptsRegistry();
98 |
99 | }
100 | }
101 |
102 | public class BitcoinStandardScriptsRegistry : StandardScriptsRegistry
103 | {
104 | // See MAX_OP_RETURN_RELAY in Bitcoin Core,
30 |