* Feat/807: ABI and classes for the contract methods * Feat/807: Added a new multisig-signer app * Feat/807: Added a new multisig-signer app * Feat/800: Untested signer forms * Feat/800: Moved reused bg video into ui-toolkit to use in multisig-signer project, and cleaned up some spacing that was overlooked in the stats theme changes * Feat/800: Componentised a bit, made the app look ok * Feat/800: Linting, prettifying, removing some unneeded tests, ensuring e2e tests run * Feat/800: Bit of translation * chore: fix type errors * chore: some parts error handling * feat: handle error and not found cases * feat: add changes to remove signer form as well * chore: rename component * chore: fix type issues * feat: add web3 connector logic * feat: allow disconnecting and show connected eth wallet info * Feat/800: Removed unused 'useApolloClient' * Feat/800: Ensure bundle.nonce and bundle.signatures have '0x' prepended * Feat/800: Removed unused e2e directory * Feat/800: Removed unnecessary app test * Feat/800: Removed unnecessary router * Feat/800: Capturing GQL errors in Sentry * Feat/800: Removing references to the unused e2e test directory * Feat/807: Consistent react hook imports * Feat/807: Removed unnecessary spreads Co-authored-by: Dexter <dexter.edwards93@gmail.com>
This commit is contained in:
parent
8fee3ca080
commit
4272fefb0b
@ -30,6 +30,10 @@ An application for the status of the Vega network. Showing block height and othe
|
|||||||
|
|
||||||
Hosting for static content being shared across apps, for example fonts.
|
Hosting for static content being shared across apps, for example fonts.
|
||||||
|
|
||||||
|
### [Multisig-signer](./apps/multisig-signer)
|
||||||
|
|
||||||
|
The utility dApp for validators wishing to add or remove themselves as a signer of the multisig contract.
|
||||||
|
|
||||||
# 🧱 Libraries in this repo
|
# 🧱 Libraries in this repo
|
||||||
|
|
||||||
### [UI toolkit](./libs/ui-toolkit)
|
### [UI toolkit](./libs/ui-toolkit)
|
||||||
|
11
apps/multisig-signer/.babelrc
Normal file
11
apps/multisig-signer/.babelrc
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"presets": [
|
||||||
|
[
|
||||||
|
"@nrwl/react/babel",
|
||||||
|
{
|
||||||
|
"runtime": "automatic"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"plugins": []
|
||||||
|
}
|
16
apps/multisig-signer/.browserslistrc
Normal file
16
apps/multisig-signer/.browserslistrc
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
# This file is used by:
|
||||||
|
# 1. autoprefixer to adjust CSS to support the below specified browsers
|
||||||
|
# 2. babel preset-env to adjust included polyfills
|
||||||
|
#
|
||||||
|
# For additional information regarding the format and rule options, please see:
|
||||||
|
# https://github.com/browserslist/browserslist#queries
|
||||||
|
#
|
||||||
|
# If you need to support different browsers in production, you may tweak the list below.
|
||||||
|
|
||||||
|
last 1 Chrome version
|
||||||
|
last 1 Firefox version
|
||||||
|
last 2 Edge major versions
|
||||||
|
last 2 Safari major version
|
||||||
|
last 2 iOS major versions
|
||||||
|
Firefox ESR
|
||||||
|
not IE 9-11 # For IE 9-11 support, remove 'not'.
|
4
apps/multisig-signer/.env
Normal file
4
apps/multisig-signer/.env
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
NX_VEGA_URL=https://api.n01.stagnet3.vega.xyz/graphql
|
||||||
|
NX_VEGA_CONFIG_URL=https://static.vega.xyz/assets/stagnet3-network.json
|
||||||
|
NX_VEGA_NETWORKS='{"TESTNET":"https://multisig-signer.fairground.wtf","MAINNET":"https://multisig-signer.vega.xyz"}'
|
||||||
|
NX_VEGA_ENV=STAGNET3
|
5
apps/multisig-signer/.env.devnet
Normal file
5
apps/multisig-signer/.env.devnet
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
# App configuration variables
|
||||||
|
NX_VEGA_CONFIG_URL=https://static.vega.xyz/assets/devnet-network.json
|
||||||
|
NX_VEGA_URL=https://api.n04.d.vega.xyz/graphql
|
||||||
|
NX_VEGA_NETWORKS='{"TESTNET":"https://multisig-signer.fairground.wtf","MAINNET":"https://multisig-signer.vega.xyz"}'
|
||||||
|
NX_VEGA_ENV=DEVNET
|
5
apps/multisig-signer/.env.mainnet
Normal file
5
apps/multisig-signer/.env.mainnet
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
# App configuration variables
|
||||||
|
NX_VEGA_CONFIG_URL=https://static.vega.xyz/assets/mainnet-network.json
|
||||||
|
NX_VEGA_URL=https://api.vega.xyz/query
|
||||||
|
NX_VEGA_NETWORKS='{"TESTNET":"https://multisig-signer.fairground.wtf","MAINNET":"https://multisig-signer.vega.xyz"}'
|
||||||
|
NX_VEGA_ENV=MAINNET
|
5
apps/multisig-signer/.env.stagnet3
Normal file
5
apps/multisig-signer/.env.stagnet3
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
# App configuration variables
|
||||||
|
NX_VEGA_CONFIG_URL=https://static.vega.xyz/assets/stagnet3-network.json
|
||||||
|
NX_VEGA_URL=https://api.n01.stagnet3.vega.xyz/graphql
|
||||||
|
NX_VEGA_NETWORKS='{"TESTNET":"https://multisig-signer.fairground.wtf","MAINNET":"https://multisig-signer.vega.xyz"}'
|
||||||
|
NX_VEGA_ENV=STAGNET3
|
5
apps/multisig-signer/.env.testnet
Normal file
5
apps/multisig-signer/.env.testnet
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
# App configuration variables
|
||||||
|
NX_VEGA_CONFIG_URL=https://static.vega.xyz/assets/testnet-network.json
|
||||||
|
NX_VEGA_URL=https://api.n09.testnet.vega.xyz/graphql
|
||||||
|
NX_VEGA_NETWORKS='{"TESTNET":"https://multisig-signer.fairground.wtf","MAINNET":"https://multisig-signer.vega.xyz"}'
|
||||||
|
NX_VEGA_ENV=TESTNET
|
18
apps/multisig-signer/.eslintrc.json
Normal file
18
apps/multisig-signer/.eslintrc.json
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
"extends": ["plugin:@nrwl/nx/react", "../../.eslintrc.json"],
|
||||||
|
"ignorePatterns": ["!**/*", "__generated__", "__generated___"],
|
||||||
|
"overrides": [
|
||||||
|
{
|
||||||
|
"files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
|
||||||
|
"rules": {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"files": ["*.ts", "*.tsx"],
|
||||||
|
"rules": {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"files": ["*.js", "*.jsx"],
|
||||||
|
"rules": {}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
46
apps/multisig-signer/README.md
Normal file
46
apps/multisig-signer/README.md
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
## Multisig-signer
|
||||||
|
|
||||||
|
## Development
|
||||||
|
|
||||||
|
First copy the configuration of the application you are starting:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cp .env.[environment] .env.local
|
||||||
|
```
|
||||||
|
|
||||||
|
Starting the app:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
yarn nx serve multisig-signer
|
||||||
|
```
|
||||||
|
|
||||||
|
### Configuration
|
||||||
|
|
||||||
|
Example configurations are provided here:
|
||||||
|
|
||||||
|
- [Mainnet](./.env.mainnet)
|
||||||
|
- [Devnet](./.env.devnet)
|
||||||
|
- [Capsule](./.env.capsule)
|
||||||
|
- [Testnet](./.env.testnet)
|
||||||
|
- [Stagnet3](./.env.stagnet3)
|
||||||
|
|
||||||
|
For convenience, you can boot the app injecting one of the configurations above by running:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
yarn nx run multisig-signer:serve --env={env} # e.g. stagnet3
|
||||||
|
```
|
||||||
|
|
||||||
|
There are a few different configuration options offered for this app:
|
||||||
|
|
||||||
|
| **Flag** | **Purpose** |
|
||||||
|
| -------------------------------- | ---------------------------------------------------------------------------------------------------- | --- | |
|
||||||
|
| `NX_VEGA_URL` | The GraphQl query endpoint of a [Vega data node](https://github.com/vegaprotocol/networks#data-node) |
|
||||||
|
| `NX_VEGA_ENV` | The name of the currently connected vega environment |
|
||||||
|
|
||||||
|
## Testing
|
||||||
|
|
||||||
|
To run the minimal set of unit tests, run the following:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
yarn nx test multisig-signer
|
||||||
|
```
|
5
apps/multisig-signer/index.d.ts
vendored
Normal file
5
apps/multisig-signer/index.d.ts
vendored
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
/// <reference types="react-scripts" />
|
||||||
|
|
||||||
|
interface Window {
|
||||||
|
_env_?: Record<string, string>;
|
||||||
|
}
|
12
apps/multisig-signer/jest.config.ts
Normal file
12
apps/multisig-signer/jest.config.ts
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
/* eslint-disable */
|
||||||
|
export default {
|
||||||
|
displayName: 'multisig-signer',
|
||||||
|
preset: '../../jest.preset.js',
|
||||||
|
transform: {
|
||||||
|
'^(?!.*\\.(js|jsx|ts|tsx|css|json)$)': '@nrwl/react/plugins/jest',
|
||||||
|
'^.+\\.[tj]sx?$': 'babel-jest',
|
||||||
|
},
|
||||||
|
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'],
|
||||||
|
coverageDirectory: '../../coverage/apps/multisig-signer',
|
||||||
|
setupFilesAfterEnv: ['./src/app/setup-tests.ts'],
|
||||||
|
};
|
4
apps/multisig-signer/netlify.toml
Normal file
4
apps/multisig-signer/netlify.toml
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
[[redirects]]
|
||||||
|
from = "/*"
|
||||||
|
to = "/index.html"
|
||||||
|
status = 200
|
10
apps/multisig-signer/postcss.config.js
Normal file
10
apps/multisig-signer/postcss.config.js
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
const { join } = require('path');
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
plugins: {
|
||||||
|
tailwindcss: {
|
||||||
|
config: join(__dirname, 'tailwind.config.js'),
|
||||||
|
},
|
||||||
|
autoprefixer: {},
|
||||||
|
},
|
||||||
|
};
|
80
apps/multisig-signer/project.json
Normal file
80
apps/multisig-signer/project.json
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
{
|
||||||
|
"$schema": "../../node_modules/nx/schemas/project-schema.json",
|
||||||
|
"sourceRoot": "apps/multisig-signer/src",
|
||||||
|
"projectType": "application",
|
||||||
|
"targets": {
|
||||||
|
"build": {
|
||||||
|
"executor": "./tools/executors/webpack:build",
|
||||||
|
"outputs": ["{options.outputPath}"],
|
||||||
|
"defaultConfiguration": "production",
|
||||||
|
"options": {
|
||||||
|
"compiler": "babel",
|
||||||
|
"outputPath": "dist/apps/multisig-signer",
|
||||||
|
"index": "apps/multisig-signer/src/index.html",
|
||||||
|
"baseHref": "/",
|
||||||
|
"main": "apps/multisig-signer/src/main.tsx",
|
||||||
|
"polyfills": "apps/multisig-signer/src/polyfills.ts",
|
||||||
|
"tsConfig": "apps/multisig-signer/tsconfig.app.json",
|
||||||
|
"assets": ["apps/multisig-signer/src/assets"],
|
||||||
|
"styles": ["apps/multisig-signer/src/styles.css"],
|
||||||
|
"scripts": [],
|
||||||
|
"webpackConfig": "apps/multisig-signer/webpack.config.js"
|
||||||
|
},
|
||||||
|
"configurations": {
|
||||||
|
"production": {
|
||||||
|
"fileReplacements": [
|
||||||
|
{
|
||||||
|
"replace": "apps/multisig-signer/src/environments/environment.ts",
|
||||||
|
"with": "apps/multisig-signer/src/environments/environment.prod.ts"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"optimization": true,
|
||||||
|
"outputHashing": "all",
|
||||||
|
"sourceMap": true,
|
||||||
|
"namedChunks": false,
|
||||||
|
"extractLicenses": true,
|
||||||
|
"vendorChunk": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"serve": {
|
||||||
|
"executor": "./tools/executors/webpack:serve",
|
||||||
|
"options": {
|
||||||
|
"port": 3000,
|
||||||
|
"buildTarget": "multisig-signer:build:development",
|
||||||
|
"hmr": true
|
||||||
|
},
|
||||||
|
"configurations": {
|
||||||
|
"production": {
|
||||||
|
"buildTarget": "multisig-signer:build:production",
|
||||||
|
"hmr": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"lint": {
|
||||||
|
"executor": "@nrwl/linter:eslint",
|
||||||
|
"outputs": ["{options.outputFile}"],
|
||||||
|
"options": {
|
||||||
|
"lintFilePatterns": ["apps/multisig-signer/**/*.{ts,tsx,js,jsx}"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"test": {
|
||||||
|
"executor": "@nrwl/jest:jest",
|
||||||
|
"outputs": ["coverage/apps/multisig-signer"],
|
||||||
|
"options": {
|
||||||
|
"jestConfig": "apps/multisig-signer/jest.config.ts",
|
||||||
|
"passWithNoTests": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"build-netlify": {
|
||||||
|
"executor": "@nrwl/workspace:run-commands",
|
||||||
|
"options": {
|
||||||
|
"commands": [
|
||||||
|
"cp apps/multisig-signer/netlify.toml netlify.toml",
|
||||||
|
"nx build multisig-signer"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"tags": []
|
||||||
|
}
|
108
apps/multisig-signer/src/app/app.tsx
Normal file
108
apps/multisig-signer/src/app/app.tsx
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
import * as Sentry from '@sentry/react';
|
||||||
|
import classnames from 'classnames';
|
||||||
|
import { useEffect, useMemo, useState } from 'react';
|
||||||
|
import { BrowserTracing } from '@sentry/tracing';
|
||||||
|
import {
|
||||||
|
EnvironmentProvider,
|
||||||
|
NetworkLoader,
|
||||||
|
useEnvironment,
|
||||||
|
} from '@vegaprotocol/environment';
|
||||||
|
import { AsyncRenderer, Button, Lozenge } from '@vegaprotocol/ui-toolkit';
|
||||||
|
import type { EthereumConfig } from '@vegaprotocol/web3';
|
||||||
|
import { useEthereumConfig, Web3Provider } from '@vegaprotocol/web3';
|
||||||
|
import { ThemeContext, useThemeSwitcher, t } from '@vegaprotocol/react-helpers';
|
||||||
|
import { createClient } from './lib/apollo-client';
|
||||||
|
import { ENV } from './config/env';
|
||||||
|
import { ContractsProvider } from './config/contracts/contracts-provider';
|
||||||
|
import {
|
||||||
|
AddSignerForm,
|
||||||
|
RemoveSignerForm,
|
||||||
|
Header,
|
||||||
|
ContractDetails,
|
||||||
|
} from './components';
|
||||||
|
import { createConnectors } from './lib/web3-connectors';
|
||||||
|
import { Web3Connector } from './components/web3-connector';
|
||||||
|
import { EthWalletContainer } from './components/eth-wallet-container';
|
||||||
|
import { useWeb3React } from '@web3-react/core';
|
||||||
|
|
||||||
|
const pageWrapperClasses = classnames(
|
||||||
|
'min-h-screen w-screen',
|
||||||
|
'grid grid-rows-[auto,1fr]',
|
||||||
|
'bg-white dark:bg-black',
|
||||||
|
'text-neutral-900 dark:text-neutral-100'
|
||||||
|
);
|
||||||
|
|
||||||
|
const ConnectedApp = ({ config }: { config: EthereumConfig | null }) => {
|
||||||
|
const { account, connector } = useWeb3React();
|
||||||
|
return (
|
||||||
|
<main className="w-full max-w-3xl px-5 justify-self-center">
|
||||||
|
<h1>{t('Multisig signer')}</h1>
|
||||||
|
<div className="mb-8">
|
||||||
|
<p>
|
||||||
|
Connected to Eth wallet: <Lozenge>{account}</Lozenge>
|
||||||
|
</p>
|
||||||
|
<Button onClick={() => connector.deactivate()}>Disconnect</Button>
|
||||||
|
</div>
|
||||||
|
<ContractDetails config={config} />
|
||||||
|
<h2>{t('Add or remove signer')}</h2>
|
||||||
|
<AddSignerForm />
|
||||||
|
<RemoveSignerForm />
|
||||||
|
</main>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
function App() {
|
||||||
|
const { VEGA_ENV, ETHEREUM_PROVIDER_URL } = useEnvironment();
|
||||||
|
const { config, loading, error } = useEthereumConfig();
|
||||||
|
const [dialogOpen, setDialogOpen] = useState(false);
|
||||||
|
const [theme, toggleTheme] = useThemeSwitcher();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
Sentry.init({
|
||||||
|
dsn: ENV.dsn,
|
||||||
|
integrations: [new BrowserTracing()],
|
||||||
|
tracesSampleRate: 1,
|
||||||
|
environment: VEGA_ENV,
|
||||||
|
});
|
||||||
|
}, [VEGA_ENV]);
|
||||||
|
const Connectors = useMemo(() => {
|
||||||
|
if (config?.chain_id) {
|
||||||
|
return createConnectors(ETHEREUM_PROVIDER_URL, Number(config.chain_id));
|
||||||
|
}
|
||||||
|
return [];
|
||||||
|
}, [config?.chain_id, ETHEREUM_PROVIDER_URL]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ThemeContext.Provider value={theme}>
|
||||||
|
<Web3Provider connectors={Connectors}>
|
||||||
|
<Web3Connector dialogOpen={dialogOpen} setDialogOpen={setDialogOpen}>
|
||||||
|
<div className={pageWrapperClasses}>
|
||||||
|
<AsyncRenderer loading={loading} data={config} error={error}>
|
||||||
|
<Header theme={theme} toggleTheme={toggleTheme} />
|
||||||
|
<EthWalletContainer
|
||||||
|
dialogOpen={dialogOpen}
|
||||||
|
setDialogOpen={setDialogOpen}
|
||||||
|
>
|
||||||
|
<ConnectedApp config={config} />
|
||||||
|
</EthWalletContainer>
|
||||||
|
</AsyncRenderer>
|
||||||
|
</div>
|
||||||
|
</Web3Connector>
|
||||||
|
</Web3Provider>
|
||||||
|
</ThemeContext.Provider>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const Wrapper = () => {
|
||||||
|
return (
|
||||||
|
<EnvironmentProvider>
|
||||||
|
<NetworkLoader createClient={createClient}>
|
||||||
|
<ContractsProvider>
|
||||||
|
<App />
|
||||||
|
</ContractsProvider>
|
||||||
|
</NetworkLoader>
|
||||||
|
</EnvironmentProvider>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Wrapper;
|
45
apps/multisig-signer/src/app/components/__generated__/AddSignerBundle.ts
generated
Normal file
45
apps/multisig-signer/src/app/components/__generated__/AddSignerBundle.ts
generated
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
/* tslint:disable */
|
||||||
|
/* eslint-disable */
|
||||||
|
// @generated
|
||||||
|
// This file was automatically generated and should not be edited.
|
||||||
|
|
||||||
|
// ====================================================
|
||||||
|
// GraphQL query operation: AddSignerBundle
|
||||||
|
// ====================================================
|
||||||
|
|
||||||
|
export interface AddSignerBundle_erc20MultiSigSignerAddedBundles_edges_node {
|
||||||
|
__typename: "ERC20MultiSigSignerAddedBundle";
|
||||||
|
/**
|
||||||
|
* The ethereum address of the signer to be added
|
||||||
|
*/
|
||||||
|
newSigner: string;
|
||||||
|
/**
|
||||||
|
* The nonce used in the signing operation
|
||||||
|
*/
|
||||||
|
nonce: string;
|
||||||
|
/**
|
||||||
|
* The bundle of signatures from current validators to sign in the new signer
|
||||||
|
*/
|
||||||
|
signatures: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AddSignerBundle_erc20MultiSigSignerAddedBundles_edges {
|
||||||
|
__typename: "ERC20MultiSigSignerAddedBundleEdge";
|
||||||
|
node: AddSignerBundle_erc20MultiSigSignerAddedBundles_edges_node;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AddSignerBundle_erc20MultiSigSignerAddedBundles {
|
||||||
|
__typename: "ERC20MultiSigSignerAddedConnection";
|
||||||
|
edges: (AddSignerBundle_erc20MultiSigSignerAddedBundles_edges | null)[] | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AddSignerBundle {
|
||||||
|
/**
|
||||||
|
* Get the signature bundle to add a particular validator to the signer list of the multisig contract
|
||||||
|
*/
|
||||||
|
erc20MultiSigSignerAddedBundles: AddSignerBundle_erc20MultiSigSignerAddedBundles;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AddSignerBundleVariables {
|
||||||
|
nodeId: string;
|
||||||
|
}
|
48
apps/multisig-signer/src/app/components/__generated__/RemoveSignerBundle.ts
generated
Normal file
48
apps/multisig-signer/src/app/components/__generated__/RemoveSignerBundle.ts
generated
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
/* tslint:disable */
|
||||||
|
/* eslint-disable */
|
||||||
|
// @generated
|
||||||
|
// This file was automatically generated and should not be edited.
|
||||||
|
|
||||||
|
// ====================================================
|
||||||
|
// GraphQL query operation: RemoveSignerBundle
|
||||||
|
// ====================================================
|
||||||
|
|
||||||
|
export interface RemoveSignerBundle_erc20MultiSigSignerRemovedBundles_edges_node {
|
||||||
|
__typename: "ERC20MultiSigSignerRemovedBundle";
|
||||||
|
/**
|
||||||
|
* The ethereum address of the signer to be removed
|
||||||
|
*/
|
||||||
|
oldSigner: string;
|
||||||
|
/**
|
||||||
|
* The nonce used in the signing operation
|
||||||
|
*/
|
||||||
|
nonce: string;
|
||||||
|
/**
|
||||||
|
* The bundle of signatures from current validators to sign in the new signer
|
||||||
|
*/
|
||||||
|
signatures: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface RemoveSignerBundle_erc20MultiSigSignerRemovedBundles_edges {
|
||||||
|
__typename: "ERC20MultiSigSignerRemovedBundleEdge";
|
||||||
|
node: RemoveSignerBundle_erc20MultiSigSignerRemovedBundles_edges_node;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface RemoveSignerBundle_erc20MultiSigSignerRemovedBundles {
|
||||||
|
__typename: "ERC20MultiSigSignerRemovedConnection";
|
||||||
|
/**
|
||||||
|
* The list of signer bundles for that validator
|
||||||
|
*/
|
||||||
|
edges: (RemoveSignerBundle_erc20MultiSigSignerRemovedBundles_edges | null)[] | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface RemoveSignerBundle {
|
||||||
|
/**
|
||||||
|
* Get the signatures bundle to remove a particular validator from signer list of the multisig contract
|
||||||
|
*/
|
||||||
|
erc20MultiSigSignerRemovedBundles: RemoveSignerBundle_erc20MultiSigSignerRemovedBundles;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface RemoveSignerBundleVariables {
|
||||||
|
nodeId: string;
|
||||||
|
}
|
@ -0,0 +1,121 @@
|
|||||||
|
import { useState } from 'react';
|
||||||
|
import { gql, useLazyQuery } from '@apollo/client';
|
||||||
|
import { captureException } from '@sentry/react';
|
||||||
|
import { t } from '@vegaprotocol/react-helpers';
|
||||||
|
import { useEthereumTransaction } from '@vegaprotocol/web3';
|
||||||
|
import {
|
||||||
|
FormGroup,
|
||||||
|
Input,
|
||||||
|
Button,
|
||||||
|
InputError,
|
||||||
|
Loader,
|
||||||
|
} from '@vegaprotocol/ui-toolkit';
|
||||||
|
import { prepend0x } from '@vegaprotocol/smart-contracts';
|
||||||
|
import { useContracts } from '../../config/contracts/contracts-context';
|
||||||
|
import type { FormEvent } from 'react';
|
||||||
|
import type {
|
||||||
|
AddSignerBundle,
|
||||||
|
AddSignerBundleVariables,
|
||||||
|
} from '../__generated__/AddSignerBundle';
|
||||||
|
import type { MultisigControl } from '@vegaprotocol/smart-contracts';
|
||||||
|
|
||||||
|
export const ADD_SIGNER_QUERY = gql`
|
||||||
|
query AddSignerBundle($nodeId: ID!) {
|
||||||
|
erc20MultiSigSignerAddedBundles(nodeId: $nodeId) {
|
||||||
|
edges {
|
||||||
|
node {
|
||||||
|
newSigner
|
||||||
|
nonce
|
||||||
|
signatures
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const AddSignerForm = () => {
|
||||||
|
const { multisig } = useContracts();
|
||||||
|
const [address, setAddress] = useState('');
|
||||||
|
const [bundleNotFound, setBundleNotFound] = useState(false);
|
||||||
|
const [runQuery, { data, error, loading }] = useLazyQuery<
|
||||||
|
AddSignerBundle,
|
||||||
|
AddSignerBundleVariables
|
||||||
|
>(ADD_SIGNER_QUERY);
|
||||||
|
const { perform, Dialog } = useEthereumTransaction<
|
||||||
|
MultisigControl,
|
||||||
|
'add_signer'
|
||||||
|
>(multisig, 'add_signer');
|
||||||
|
const handleSubmit = async (e: FormEvent<HTMLFormElement>) => {
|
||||||
|
e.preventDefault();
|
||||||
|
setBundleNotFound(false);
|
||||||
|
try {
|
||||||
|
if (address === '') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await runQuery({
|
||||||
|
variables: { nodeId: address },
|
||||||
|
});
|
||||||
|
const bundle = data?.erc20MultiSigSignerAddedBundles?.edges?.[0]?.node;
|
||||||
|
|
||||||
|
if (!bundle) {
|
||||||
|
if (!error) {
|
||||||
|
setBundleNotFound(true);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await perform(
|
||||||
|
bundle.newSigner,
|
||||||
|
bundle.nonce.startsWith('0x') ? bundle.nonce : prepend0x(bundle.nonce),
|
||||||
|
bundle.signatures.startsWith('0x')
|
||||||
|
? bundle.signatures
|
||||||
|
: prepend0x(bundle.signatures)
|
||||||
|
);
|
||||||
|
} catch (err: unknown) {
|
||||||
|
captureException(err);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<form onSubmit={(e) => handleSubmit(e)}>
|
||||||
|
<FormGroup
|
||||||
|
label={t('Add signer')}
|
||||||
|
labelFor="add-signer-input"
|
||||||
|
labelDescription={t('Public key of the signer to add')}
|
||||||
|
className="max-w-xl"
|
||||||
|
>
|
||||||
|
<div className="grid grid-cols-[1fr,auto] gap-2">
|
||||||
|
<Input
|
||||||
|
id="add-signer-input"
|
||||||
|
onChange={(e) => setAddress(e.target.value)}
|
||||||
|
data-testid="add-signer-input-input"
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
type="submit"
|
||||||
|
data-testid="add-signer-submit"
|
||||||
|
disabled={loading}
|
||||||
|
>
|
||||||
|
{loading ? <Loader size="small" /> : t('Add')}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
{error && (
|
||||||
|
<InputError intent="danger">
|
||||||
|
{error?.message.includes('InvalidArgument')
|
||||||
|
? t('Invalid node id')
|
||||||
|
: error?.message}
|
||||||
|
</InputError>
|
||||||
|
)}
|
||||||
|
{bundleNotFound && !error && (
|
||||||
|
<InputError intent="danger">
|
||||||
|
{t(
|
||||||
|
'Bundle was not found, are you sure this validator needs to be added?'
|
||||||
|
)}
|
||||||
|
</InputError>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</FormGroup>
|
||||||
|
<Dialog />
|
||||||
|
</form>
|
||||||
|
);
|
||||||
|
};
|
@ -0,0 +1 @@
|
|||||||
|
export * from './add-signer-form';
|
@ -0,0 +1,35 @@
|
|||||||
|
import { useEffect, useState } from 'react';
|
||||||
|
import * as Sentry from '@sentry/react';
|
||||||
|
import { Lozenge } from '@vegaprotocol/ui-toolkit';
|
||||||
|
import { useContracts } from '../../config/contracts/contracts-context';
|
||||||
|
import type { EthereumConfig } from '@vegaprotocol/web3';
|
||||||
|
|
||||||
|
interface ContractDetailsProps {
|
||||||
|
config: EthereumConfig | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const ContractDetails = ({ config }: ContractDetailsProps) => {
|
||||||
|
const { multisig } = useContracts();
|
||||||
|
const [validSignerCount, setValidSignerCount] = useState(undefined);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
(async () => {
|
||||||
|
try {
|
||||||
|
const res = await multisig.get_valid_signer_count();
|
||||||
|
setValidSignerCount(res);
|
||||||
|
} catch (err) {
|
||||||
|
Sentry.captureException(err);
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
}, [multisig]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="mb-8">
|
||||||
|
<p>
|
||||||
|
Multisig contract address:{' '}
|
||||||
|
<Lozenge>{config?.multisig_control_contract?.address}</Lozenge>
|
||||||
|
</p>
|
||||||
|
<p>Valid signer count: {validSignerCount}</p>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
@ -0,0 +1 @@
|
|||||||
|
export * from './contract-details';
|
@ -0,0 +1,25 @@
|
|||||||
|
import { Button } from '@vegaprotocol/ui-toolkit';
|
||||||
|
import { useWeb3React } from '@web3-react/core';
|
||||||
|
import type { ReactElement } from 'react';
|
||||||
|
|
||||||
|
export const EthWalletContainer = ({
|
||||||
|
dialogOpen,
|
||||||
|
setDialogOpen,
|
||||||
|
children,
|
||||||
|
}: {
|
||||||
|
dialogOpen: boolean;
|
||||||
|
setDialogOpen: (open: boolean) => void;
|
||||||
|
children: ReactElement;
|
||||||
|
}) => {
|
||||||
|
const { account } = useWeb3React();
|
||||||
|
if (!account) {
|
||||||
|
return (
|
||||||
|
<div className="w-full text-center">
|
||||||
|
<Button onClick={() => setDialogOpen(true)}>
|
||||||
|
Connect Ethereum Wallet
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return children;
|
||||||
|
};
|
@ -0,0 +1 @@
|
|||||||
|
export * from './eth-wallet-container';
|
24
apps/multisig-signer/src/app/components/header/header.tsx
Normal file
24
apps/multisig-signer/src/app/components/header/header.tsx
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
import {
|
||||||
|
BackgroundVideo,
|
||||||
|
ThemeSwitcher,
|
||||||
|
VegaLogo,
|
||||||
|
} from '@vegaprotocol/ui-toolkit';
|
||||||
|
|
||||||
|
interface HeaderProps {
|
||||||
|
theme: 'light' | 'dark';
|
||||||
|
toggleTheme: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const Header = ({ theme, toggleTheme }: HeaderProps) => {
|
||||||
|
return (
|
||||||
|
<header className="relative overflow-hidden py-2 mb-8">
|
||||||
|
<BackgroundVideo />
|
||||||
|
<div className="relative flex justify-center px-2 dark:bg-black bg-white">
|
||||||
|
<div className="w-full max-w-3xl p-5 flex items-center justify-between">
|
||||||
|
<VegaLogo />
|
||||||
|
<ThemeSwitcher theme={theme} onToggle={toggleTheme} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
);
|
||||||
|
};
|
1
apps/multisig-signer/src/app/components/header/index.tsx
Normal file
1
apps/multisig-signer/src/app/components/header/index.tsx
Normal file
@ -0,0 +1 @@
|
|||||||
|
export * from './header';
|
4
apps/multisig-signer/src/app/components/index.tsx
Normal file
4
apps/multisig-signer/src/app/components/index.tsx
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
export * from './add-signer-form';
|
||||||
|
export * from './remove-signer-form';
|
||||||
|
export * from './header';
|
||||||
|
export * from './contract-details';
|
@ -0,0 +1 @@
|
|||||||
|
export * from './remove-signer-form';
|
@ -0,0 +1,121 @@
|
|||||||
|
import { useState } from 'react';
|
||||||
|
import { gql, useLazyQuery } from '@apollo/client';
|
||||||
|
import { captureException } from '@sentry/react';
|
||||||
|
import { t } from '@vegaprotocol/react-helpers';
|
||||||
|
import { useEthereumTransaction } from '@vegaprotocol/web3';
|
||||||
|
import {
|
||||||
|
FormGroup,
|
||||||
|
Input,
|
||||||
|
Button,
|
||||||
|
InputError,
|
||||||
|
Loader,
|
||||||
|
} from '@vegaprotocol/ui-toolkit';
|
||||||
|
import { prepend0x } from '@vegaprotocol/smart-contracts';
|
||||||
|
import { useContracts } from '../../config/contracts/contracts-context';
|
||||||
|
import type { FormEvent } from 'react';
|
||||||
|
import type {
|
||||||
|
RemoveSignerBundle,
|
||||||
|
RemoveSignerBundleVariables,
|
||||||
|
} from '../__generated__/RemoveSignerBundle';
|
||||||
|
import type { MultisigControl } from '@vegaprotocol/smart-contracts';
|
||||||
|
|
||||||
|
const REMOVE_SIGNER_QUERY = gql`
|
||||||
|
query RemoveSignerBundle($nodeId: ID!) {
|
||||||
|
erc20MultiSigSignerRemovedBundles(nodeId: $nodeId) {
|
||||||
|
edges {
|
||||||
|
node {
|
||||||
|
oldSigner
|
||||||
|
nonce
|
||||||
|
signatures
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const RemoveSignerForm = () => {
|
||||||
|
const { multisig } = useContracts();
|
||||||
|
const [address, setAddress] = useState('');
|
||||||
|
const [bundleNotFound, setBundleNotFound] = useState(false);
|
||||||
|
const [runQuery, { data, error, loading }] = useLazyQuery<
|
||||||
|
RemoveSignerBundle,
|
||||||
|
RemoveSignerBundleVariables
|
||||||
|
>(REMOVE_SIGNER_QUERY);
|
||||||
|
const { perform, Dialog } = useEthereumTransaction<
|
||||||
|
MultisigControl,
|
||||||
|
'remove_signer'
|
||||||
|
>(multisig, 'remove_signer');
|
||||||
|
const handleSubmit = async (e: FormEvent<HTMLFormElement>) => {
|
||||||
|
e.preventDefault();
|
||||||
|
setBundleNotFound(false);
|
||||||
|
try {
|
||||||
|
if (address === '') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await runQuery({
|
||||||
|
variables: { nodeId: address },
|
||||||
|
});
|
||||||
|
const bundle = data?.erc20MultiSigSignerRemovedBundles?.edges?.[0]?.node;
|
||||||
|
|
||||||
|
if (!bundle) {
|
||||||
|
if (!error) {
|
||||||
|
setBundleNotFound(true);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await perform(
|
||||||
|
bundle.oldSigner,
|
||||||
|
bundle.nonce.startsWith('0x') ? bundle.nonce : prepend0x(bundle.nonce),
|
||||||
|
bundle.signatures.startsWith('0x')
|
||||||
|
? bundle.signatures
|
||||||
|
: prepend0x(bundle.signatures)
|
||||||
|
);
|
||||||
|
} catch (err: unknown) {
|
||||||
|
captureException(err);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<form onSubmit={(e) => handleSubmit(e)}>
|
||||||
|
<FormGroup
|
||||||
|
label={t('Remove signer')}
|
||||||
|
labelFor="remove-signer-input"
|
||||||
|
labelDescription={t('Public key of the signer to remove')}
|
||||||
|
className="max-w-xl"
|
||||||
|
>
|
||||||
|
<div className="grid grid-cols-[1fr,auto] gap-2">
|
||||||
|
<Input
|
||||||
|
id="remove-signer-input"
|
||||||
|
onChange={(e) => setAddress(e.target.value)}
|
||||||
|
data-testid="remove-signer-input-input"
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
type="submit"
|
||||||
|
data-testid="remove-signer-submit"
|
||||||
|
disabled={loading}
|
||||||
|
>
|
||||||
|
{loading ? <Loader size="small" /> : t('Remove')}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
{error && (
|
||||||
|
<InputError intent="danger">
|
||||||
|
{error?.message.includes('InvalidArgument')
|
||||||
|
? t('Invalid node id')
|
||||||
|
: error?.message}
|
||||||
|
</InputError>
|
||||||
|
)}
|
||||||
|
{bundleNotFound && !error && (
|
||||||
|
<InputError intent="danger">
|
||||||
|
{t(
|
||||||
|
'Bundle was not found, are you sure this validator needs to be removed?'
|
||||||
|
)}
|
||||||
|
</InputError>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</FormGroup>
|
||||||
|
<Dialog />
|
||||||
|
</form>
|
||||||
|
);
|
||||||
|
};
|
@ -0,0 +1 @@
|
|||||||
|
export * from './web3-connector';
|
@ -0,0 +1,94 @@
|
|||||||
|
import { useEnvironment } from '@vegaprotocol/environment';
|
||||||
|
import { useEthereumConfig } from '@vegaprotocol/web3';
|
||||||
|
import { Button, Splash, AsyncRenderer } from '@vegaprotocol/ui-toolkit';
|
||||||
|
import { Web3ConnectDialog } from '@vegaprotocol/web3';
|
||||||
|
import { useWeb3React } from '@web3-react/core';
|
||||||
|
import type { ReactElement } from 'react';
|
||||||
|
import { useEffect, useMemo } from 'react';
|
||||||
|
import { createConnectors } from '../../lib/web3-connectors';
|
||||||
|
|
||||||
|
interface Web3ConnectorProps {
|
||||||
|
children: ReactElement;
|
||||||
|
dialogOpen: boolean;
|
||||||
|
setDialogOpen: (open: boolean) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function Web3Connector({
|
||||||
|
children,
|
||||||
|
dialogOpen,
|
||||||
|
setDialogOpen,
|
||||||
|
}: Web3ConnectorProps) {
|
||||||
|
const { ETHEREUM_PROVIDER_URL } = useEnvironment();
|
||||||
|
const { config, loading, error } = useEthereumConfig();
|
||||||
|
const Connectors = useMemo(() => {
|
||||||
|
if (config?.chain_id) {
|
||||||
|
return createConnectors(ETHEREUM_PROVIDER_URL, Number(config.chain_id));
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
}, [config?.chain_id, ETHEREUM_PROVIDER_URL]);
|
||||||
|
const appChainId = Number(config?.chain_id);
|
||||||
|
return (
|
||||||
|
<AsyncRenderer loading={loading} error={error} data={config}>
|
||||||
|
<Web3Content appChainId={appChainId} setDialogOpen={setDialogOpen}>
|
||||||
|
{children}
|
||||||
|
</Web3Content>
|
||||||
|
{Connectors && (
|
||||||
|
<Web3ConnectDialog
|
||||||
|
connectors={Connectors}
|
||||||
|
dialogOpen={dialogOpen}
|
||||||
|
setDialogOpen={setDialogOpen}
|
||||||
|
desiredChainId={appChainId}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</AsyncRenderer>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Web3ContentProps {
|
||||||
|
children: ReactElement;
|
||||||
|
appChainId: number;
|
||||||
|
setDialogOpen: (isOpen: boolean) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const Web3Content = ({
|
||||||
|
children,
|
||||||
|
appChainId,
|
||||||
|
setDialogOpen,
|
||||||
|
}: Web3ContentProps) => {
|
||||||
|
const { error, connector, chainId } = useWeb3React();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (connector?.connectEagerly) {
|
||||||
|
connector.connectEagerly();
|
||||||
|
}
|
||||||
|
// wallet connect doesnt handle connectEagerly being called when connector is also in the
|
||||||
|
// deps array.
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, [connector]);
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
return (
|
||||||
|
<Splash>
|
||||||
|
<div className="flex flex-col items-center gap-12">
|
||||||
|
<p className="text-white">Something went wrong: {error.message}</p>
|
||||||
|
<Button onClick={() => connector.deactivate()}>Disconnect</Button>
|
||||||
|
</div>
|
||||||
|
</Splash>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (chainId !== undefined && chainId !== appChainId) {
|
||||||
|
return (
|
||||||
|
<Splash>
|
||||||
|
<div className="flex flex-col items-center gap-12">
|
||||||
|
<p className="text-white">
|
||||||
|
This app only works on chain ID: {appChainId}
|
||||||
|
</p>
|
||||||
|
<Button onClick={() => connector.deactivate()}>Disconnect</Button>
|
||||||
|
</div>
|
||||||
|
</Splash>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return children;
|
||||||
|
};
|
@ -0,0 +1,18 @@
|
|||||||
|
import { createContext, useContext } from 'react';
|
||||||
|
import type { MultisigControl } from '@vegaprotocol/smart-contracts';
|
||||||
|
|
||||||
|
export interface ContractsContextShape {
|
||||||
|
multisig: MultisigControl;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const ContractsContext = createContext<
|
||||||
|
ContractsContextShape | undefined
|
||||||
|
>(undefined);
|
||||||
|
|
||||||
|
export function useContracts() {
|
||||||
|
const context = useContext(ContractsContext);
|
||||||
|
if (context === undefined) {
|
||||||
|
throw new Error('useContracts must be used within ContractsProvider');
|
||||||
|
}
|
||||||
|
return context;
|
||||||
|
}
|
@ -0,0 +1,60 @@
|
|||||||
|
import { useEffect, useState } from 'react';
|
||||||
|
import { MultisigControl } from '@vegaprotocol/smart-contracts';
|
||||||
|
import { Splash } from '@vegaprotocol/ui-toolkit';
|
||||||
|
import { ethers } from 'ethers';
|
||||||
|
import type { ContractsContextShape } from './contracts-context';
|
||||||
|
import { ContractsContext } from './contracts-context';
|
||||||
|
import { useEthereumConfig } from '@vegaprotocol/web3';
|
||||||
|
import { useEnvironment } from '@vegaprotocol/environment';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides Vega Ethereum contract instances to its children.
|
||||||
|
*/
|
||||||
|
export const ContractsProvider = ({ children }: { children: JSX.Element }) => {
|
||||||
|
const { config } = useEthereumConfig();
|
||||||
|
const { VEGA_ENV, ETHEREUM_PROVIDER_URL } = useEnvironment();
|
||||||
|
const [contracts, setContracts] = useState<ContractsContextShape | null>(
|
||||||
|
null
|
||||||
|
);
|
||||||
|
|
||||||
|
// Create instances of contract classes. If we have an account use a signer for the
|
||||||
|
// contracts so that we can sign transactions, otherwise use the provider for just
|
||||||
|
// reading data
|
||||||
|
useEffect(() => {
|
||||||
|
let cancelled = false;
|
||||||
|
const run = async () => {
|
||||||
|
if (config) {
|
||||||
|
const provider = new ethers.providers.JsonRpcProvider(
|
||||||
|
ETHEREUM_PROVIDER_URL,
|
||||||
|
Number(config.chain_id)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (provider && config) {
|
||||||
|
if (!cancelled) {
|
||||||
|
setContracts({
|
||||||
|
multisig: new MultisigControl(
|
||||||
|
config.multisig_control_contract.address,
|
||||||
|
provider
|
||||||
|
),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
run();
|
||||||
|
return () => {
|
||||||
|
// TODO: hacky quick fix for release to prevent race condition, find a better fix for this.
|
||||||
|
cancelled = true;
|
||||||
|
};
|
||||||
|
}, [config, VEGA_ENV, ETHEREUM_PROVIDER_URL]);
|
||||||
|
|
||||||
|
if (!contracts) {
|
||||||
|
return <Splash>Error: cannot get data on multisig contract</Splash>;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ContractsContext.Provider value={contracts}>
|
||||||
|
{children}
|
||||||
|
</ContractsContext.Provider>
|
||||||
|
);
|
||||||
|
};
|
12
apps/multisig-signer/src/app/config/env.ts
Normal file
12
apps/multisig-signer/src/app/config/env.ts
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
const windowOrDefault = (key: string) => {
|
||||||
|
if (window._env_ && window._env_[key]) {
|
||||||
|
return window._env_[key] as string;
|
||||||
|
}
|
||||||
|
return (process.env[key] as string) || '';
|
||||||
|
};
|
||||||
|
|
||||||
|
export const ENV = {
|
||||||
|
dsn: windowOrDefault('NX_SENTRY_DSN'),
|
||||||
|
flags: {},
|
||||||
|
dataSources: {},
|
||||||
|
};
|
3
apps/multisig-signer/src/app/config/flags.ts
Normal file
3
apps/multisig-signer/src/app/config/flags.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
import { ENV } from './env';
|
||||||
|
|
||||||
|
export default ENV.flags;
|
3
apps/multisig-signer/src/app/config/index.tsx
Normal file
3
apps/multisig-signer/src/app/config/index.tsx
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
import { ENV } from './env';
|
||||||
|
|
||||||
|
export const DATA_SOURCES = ENV.dataSources;
|
54
apps/multisig-signer/src/app/lib/apollo-client.tsx
Normal file
54
apps/multisig-signer/src/app/lib/apollo-client.tsx
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
import * as Sentry from '@sentry/react';
|
||||||
|
import { ApolloClient, from, HttpLink, InMemoryCache } from '@apollo/client';
|
||||||
|
import { onError } from '@apollo/client/link/error';
|
||||||
|
import { RetryLink } from '@apollo/client/link/retry';
|
||||||
|
|
||||||
|
export function createClient(base?: string) {
|
||||||
|
if (!base) {
|
||||||
|
throw new Error('Base must be passed into createClient!');
|
||||||
|
}
|
||||||
|
const urlHTTP = new URL(base);
|
||||||
|
const urlWS = new URL(base);
|
||||||
|
// Replace http with ws, preserving if its a secure connection eg. https => wss
|
||||||
|
urlWS.protocol = urlWS.protocol.replace('http', 'ws');
|
||||||
|
|
||||||
|
const cache = new InMemoryCache({
|
||||||
|
typePolicies: {
|
||||||
|
Query: {},
|
||||||
|
Account: {
|
||||||
|
keyFields: false,
|
||||||
|
fields: {
|
||||||
|
balanceFormatted: {},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Node: {
|
||||||
|
keyFields: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const retryLink = new RetryLink({
|
||||||
|
delay: {
|
||||||
|
initial: 300,
|
||||||
|
max: 10000,
|
||||||
|
jitter: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const httpLink = new HttpLink({
|
||||||
|
uri: urlHTTP.href,
|
||||||
|
credentials: 'same-origin',
|
||||||
|
});
|
||||||
|
|
||||||
|
const errorLink = onError(({ graphQLErrors, networkError }) => {
|
||||||
|
console.log(graphQLErrors);
|
||||||
|
console.log(networkError);
|
||||||
|
Sentry.captureException(graphQLErrors);
|
||||||
|
});
|
||||||
|
|
||||||
|
return new ApolloClient({
|
||||||
|
connectToDevTools: process.env['NODE_ENV'] === 'development',
|
||||||
|
link: from([errorLink, retryLink, httpLink]),
|
||||||
|
cache,
|
||||||
|
});
|
||||||
|
}
|
34
apps/multisig-signer/src/app/lib/web3-connectors.ts
Normal file
34
apps/multisig-signer/src/app/lib/web3-connectors.ts
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
import { ethers } from 'ethers';
|
||||||
|
import type { Web3ReactHooks } from '@web3-react/core';
|
||||||
|
import { initializeConnector } from '@web3-react/core';
|
||||||
|
import { MetaMask } from '@web3-react/metamask';
|
||||||
|
import { WalletConnect } from '@web3-react/walletconnect';
|
||||||
|
import type { Connector } from '@web3-react/types';
|
||||||
|
|
||||||
|
const [metamask, metamaskHooks] = initializeConnector<MetaMask>(
|
||||||
|
(actions) => new MetaMask(actions)
|
||||||
|
);
|
||||||
|
|
||||||
|
export const createDefaultProvider = (providerUrl: string, chainId: number) => {
|
||||||
|
return new ethers.providers.JsonRpcProvider(providerUrl, chainId);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const createConnectors = (providerUrl: string, chainId: number) => {
|
||||||
|
if (isNaN(chainId)) {
|
||||||
|
throw new Error('Invalid Ethereum chain ID for environment');
|
||||||
|
}
|
||||||
|
const [walletconnect, walletconnectHooks] =
|
||||||
|
initializeConnector<WalletConnect>(
|
||||||
|
(actions) =>
|
||||||
|
new WalletConnect(actions, {
|
||||||
|
rpc: {
|
||||||
|
[chainId]: providerUrl,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
[chainId]
|
||||||
|
);
|
||||||
|
return [
|
||||||
|
[metamask, metamaskHooks],
|
||||||
|
[walletconnect, walletconnectHooks],
|
||||||
|
] as [Connector, Web3ReactHooks][];
|
||||||
|
};
|
1
apps/multisig-signer/src/app/react-app-env.d.ts
vendored
Normal file
1
apps/multisig-signer/src/app/react-app-env.d.ts
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
/// <reference types="react-scripts" />
|
5
apps/multisig-signer/src/app/setup-tests.ts
Normal file
5
apps/multisig-signer/src/app/setup-tests.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
// jest-dom adds custom jest matchers for asserting on DOM nodes.
|
||||||
|
// allows you to do things like:
|
||||||
|
// expect(element).toHaveTextContent(/react/i)
|
||||||
|
// learn more: https://github.com/testing-library/jest-dom
|
||||||
|
import '@testing-library/jest-dom';
|
0
apps/multisig-signer/src/assets/.gitkeep
Normal file
0
apps/multisig-signer/src/assets/.gitkeep
Normal file
1
apps/multisig-signer/src/assets/env-config.js
Normal file
1
apps/multisig-signer/src/assets/env-config.js
Normal file
@ -0,0 +1 @@
|
|||||||
|
window._env_ = {};
|
@ -0,0 +1,3 @@
|
|||||||
|
export const environment = {
|
||||||
|
production: true,
|
||||||
|
};
|
6
apps/multisig-signer/src/environments/environment.ts
Normal file
6
apps/multisig-signer/src/environments/environment.ts
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
// This file can be replaced during build by using the `fileReplacements` array.
|
||||||
|
// When building for production, this file is replaced with `environment.prod.ts`.
|
||||||
|
|
||||||
|
export const environment = {
|
||||||
|
production: false,
|
||||||
|
};
|
BIN
apps/multisig-signer/src/favicon.ico
Normal file
BIN
apps/multisig-signer/src/favicon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 7.2 KiB |
26
apps/multisig-signer/src/index.html
Normal file
26
apps/multisig-signer/src/index.html
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<title>Multisig Signer</title>
|
||||||
|
<base href="/" />
|
||||||
|
<link
|
||||||
|
rel="preload"
|
||||||
|
href="https://static.vega.xyz/AlphaLyrae-Medium.woff2"
|
||||||
|
as="font"
|
||||||
|
type="font/woff2"
|
||||||
|
crossorigin="anonymous"
|
||||||
|
/>
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
<link
|
||||||
|
rel="icon"
|
||||||
|
type="image/x-icon"
|
||||||
|
href="https://static.vega.xyz/favicon.ico"
|
||||||
|
/>
|
||||||
|
<link rel="stylesheet" href="https://static.vega.xyz/fonts.css" />
|
||||||
|
<script src="./assets/env-config.js"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="root"></div>
|
||||||
|
</body>
|
||||||
|
</html>
|
14
apps/multisig-signer/src/main.tsx
Normal file
14
apps/multisig-signer/src/main.tsx
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
import { createRoot } from 'react-dom/client';
|
||||||
|
import './styles.css';
|
||||||
|
|
||||||
|
import App from './app/app';
|
||||||
|
import { StrictMode } from 'react';
|
||||||
|
|
||||||
|
const rootElement = document.getElementById('root');
|
||||||
|
const root = rootElement && createRoot(rootElement);
|
||||||
|
|
||||||
|
root?.render(
|
||||||
|
<StrictMode>
|
||||||
|
<App />
|
||||||
|
</StrictMode>
|
||||||
|
);
|
7
apps/multisig-signer/src/polyfills.ts
Normal file
7
apps/multisig-signer/src/polyfills.ts
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
/**
|
||||||
|
* Polyfill stable language features. These imports will be optimized by `@babel/preset-env`.
|
||||||
|
*
|
||||||
|
* See: https://github.com/zloirock/core-js#babel
|
||||||
|
*/
|
||||||
|
import 'core-js/stable';
|
||||||
|
import 'regenerator-runtime/runtime';
|
16
apps/multisig-signer/src/styles.css
Normal file
16
apps/multisig-signer/src/styles.css
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
/* You can add global styles to this file, and also import other style files */
|
||||||
|
@tailwind base;
|
||||||
|
@tailwind components;
|
||||||
|
@tailwind utilities;
|
||||||
|
|
||||||
|
@layer base {
|
||||||
|
h1 {
|
||||||
|
@apply text-2xl uppercase mb-4;
|
||||||
|
}
|
||||||
|
h2 {
|
||||||
|
@apply text-xl mb-4;
|
||||||
|
}
|
||||||
|
p {
|
||||||
|
@apply mb-2;
|
||||||
|
}
|
||||||
|
}
|
17
apps/multisig-signer/tailwind.config.js
Normal file
17
apps/multisig-signer/tailwind.config.js
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
const { join } = require('path');
|
||||||
|
const { createGlobPatternsForDependencies } = require('@nrwl/next/tailwind');
|
||||||
|
const theme = require('../../libs/tailwindcss-config/src/theme');
|
||||||
|
const vegaCustomClasses = require('../../libs/tailwindcss-config/src/vega-custom-classes');
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
content: [
|
||||||
|
join(__dirname, 'src/**/*.{js,ts,jsx,tsx}'),
|
||||||
|
'libs/ui-toolkit/src/utils/shared.ts',
|
||||||
|
...createGlobPatternsForDependencies(__dirname),
|
||||||
|
],
|
||||||
|
darkMode: 'class',
|
||||||
|
theme: {
|
||||||
|
extend: theme,
|
||||||
|
},
|
||||||
|
plugins: [vegaCustomClasses],
|
||||||
|
};
|
23
apps/multisig-signer/tsconfig.app.json
Normal file
23
apps/multisig-signer/tsconfig.app.json
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"extends": "./tsconfig.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"outDir": "../../dist/out-tsc",
|
||||||
|
"types": ["node"]
|
||||||
|
},
|
||||||
|
"files": [
|
||||||
|
"../../node_modules/@nrwl/react/typings/cssmodule.d.ts",
|
||||||
|
"../../node_modules/@nrwl/react/typings/image.d.ts"
|
||||||
|
],
|
||||||
|
"exclude": [
|
||||||
|
"**/*.spec.ts",
|
||||||
|
"**/*.test.ts",
|
||||||
|
"**/*.spec.tsx",
|
||||||
|
"**/*.test.tsx",
|
||||||
|
"**/*.spec.js",
|
||||||
|
"**/*.test.js",
|
||||||
|
"**/*.spec.jsx",
|
||||||
|
"**/*.test.jsx",
|
||||||
|
"jest.config.ts"
|
||||||
|
],
|
||||||
|
"include": ["**/*.js", "**/*.jsx", "**/*.ts", "**/*.tsx"]
|
||||||
|
}
|
26
apps/multisig-signer/tsconfig.json
Normal file
26
apps/multisig-signer/tsconfig.json
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
{
|
||||||
|
"extends": "../../tsconfig.base.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"jsx": "react-jsx",
|
||||||
|
"allowJs": true,
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"allowSyntheticDefaultImports": true,
|
||||||
|
"forceConsistentCasingInFileNames": true,
|
||||||
|
"strict": true,
|
||||||
|
"noImplicitOverride": true,
|
||||||
|
"noPropertyAccessFromIndexSignature": false,
|
||||||
|
"noImplicitReturns": true,
|
||||||
|
"noFallthroughCasesInSwitch": true,
|
||||||
|
"lib": ["es5", "es6", "dom", "dom.iterable"]
|
||||||
|
},
|
||||||
|
"files": [],
|
||||||
|
"include": [],
|
||||||
|
"references": [
|
||||||
|
{
|
||||||
|
"path": "./tsconfig.app.json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "./tsconfig.spec.json"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
24
apps/multisig-signer/tsconfig.spec.json
Normal file
24
apps/multisig-signer/tsconfig.spec.json
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
{
|
||||||
|
"extends": "./tsconfig.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"outDir": "../../dist/out-tsc",
|
||||||
|
"module": "commonjs",
|
||||||
|
"types": ["jest", "node", "@testing-library/jest-dom"]
|
||||||
|
},
|
||||||
|
"include": [
|
||||||
|
"**/*.test.ts",
|
||||||
|
"**/*.spec.ts",
|
||||||
|
"**/*.test.tsx",
|
||||||
|
"**/*.spec.tsx",
|
||||||
|
"**/*.test.js",
|
||||||
|
"**/*.spec.js",
|
||||||
|
"**/*.test.jsx",
|
||||||
|
"**/*.spec.jsx",
|
||||||
|
"**/*.d.ts",
|
||||||
|
"jest.config.ts"
|
||||||
|
],
|
||||||
|
"files": [
|
||||||
|
"../../node_modules/@nrwl/react/typings/cssmodule.d.ts",
|
||||||
|
"../../node_modules/@nrwl/react/typings/image.d.ts"
|
||||||
|
]
|
||||||
|
}
|
17
apps/multisig-signer/webpack.config.js
Normal file
17
apps/multisig-signer/webpack.config.js
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
const SentryPlugin = require('@sentry/webpack-plugin');
|
||||||
|
|
||||||
|
module.exports = (config, context) => {
|
||||||
|
const additionalPlugins = process.env.SENTRY_AUTH_TOKEN
|
||||||
|
? [
|
||||||
|
new SentryPlugin({
|
||||||
|
include: './dist/apps/multisig-signer',
|
||||||
|
project: 'multisig-signer',
|
||||||
|
}),
|
||||||
|
]
|
||||||
|
: [];
|
||||||
|
|
||||||
|
return {
|
||||||
|
...config,
|
||||||
|
plugins: [...additionalPlugins, ...config.plugins],
|
||||||
|
};
|
||||||
|
};
|
@ -11,10 +11,10 @@ function App() {
|
|||||||
return (
|
return (
|
||||||
<ThemeContext.Provider value={theme}>
|
<ThemeContext.Provider value={theme}>
|
||||||
<NetworkLoader createClient={createClient}>
|
<NetworkLoader createClient={createClient}>
|
||||||
<div className="w-screen min-h-screen grid pb-24 bg-white text-neutral-900 dark:bg-black dark:text-neutral-100">
|
<div className="w-screen min-h-screen grid pb-6 bg-white text-neutral-900 dark:bg-black dark:text-neutral-100">
|
||||||
<div className="layout-grid w-screen justify-self-center">
|
<div className="layout-grid w-screen justify-self-center">
|
||||||
<Header theme={theme} toggleTheme={toggleTheme} />
|
<Header theme={theme} toggleTheme={toggleTheme} />
|
||||||
<StatsManager className="max-w-3xl px-24" />
|
<StatsManager className="max-w-3xl px-6" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</NetworkLoader>
|
</NetworkLoader>
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
import { VegaLogo, ThemeSwitcher } from '@vegaprotocol/ui-toolkit';
|
import {
|
||||||
import { VegaBackgroundVideo } from '../videos';
|
BackgroundVideo,
|
||||||
|
VegaLogo,
|
||||||
|
ThemeSwitcher,
|
||||||
|
} from '@vegaprotocol/ui-toolkit';
|
||||||
|
|
||||||
interface ThemeToggleProps {
|
interface ThemeToggleProps {
|
||||||
theme: 'light' | 'dark';
|
theme: 'light' | 'dark';
|
||||||
@ -8,11 +11,11 @@ interface ThemeToggleProps {
|
|||||||
|
|
||||||
export const Header = ({ theme, toggleTheme }: ThemeToggleProps) => {
|
export const Header = ({ theme, toggleTheme }: ThemeToggleProps) => {
|
||||||
return (
|
return (
|
||||||
<header className="relative overflow-hidden py-8 mb-40 md:mb-64">
|
<header className="relative overflow-hidden py-2 mb-10 md:mb-16">
|
||||||
<VegaBackgroundVideo />
|
<BackgroundVideo />
|
||||||
|
|
||||||
<div className="relative flex justify-center px-8 dark:bg-black bg-white">
|
<div className="relative flex justify-center px-2 dark:bg-black bg-white">
|
||||||
<div className="w-full max-w-3xl p-20 flex items-center justify-between">
|
<div className="w-full max-w-3xl p-5 flex items-center justify-between">
|
||||||
<VegaLogo />
|
<VegaLogo />
|
||||||
<ThemeSwitcher theme={theme} onToggle={toggleTheme} />
|
<ThemeSwitcher theme={theme} onToggle={toggleTheme} />
|
||||||
</div>
|
</div>
|
||||||
|
@ -1 +0,0 @@
|
|||||||
export { VegaBackgroundVideo } from './vega-background-video';
|
|
276
libs/smart-contracts/src/abis/multisig_abi.json
Normal file
276
libs/smart-contracts/src/abis/multisig_abi.json
Normal file
@ -0,0 +1,276 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"inputs": [],
|
||||||
|
"stateMutability": "nonpayable",
|
||||||
|
"type": "constructor"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"anonymous": false,
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"indexed": false,
|
||||||
|
"internalType": "uint256",
|
||||||
|
"name": "nonce",
|
||||||
|
"type": "uint256"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name": "NonceBurnt",
|
||||||
|
"type": "event"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"anonymous": false,
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"indexed": false,
|
||||||
|
"internalType": "address",
|
||||||
|
"name": "new_signer",
|
||||||
|
"type": "address"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"indexed": false,
|
||||||
|
"internalType": "uint256",
|
||||||
|
"name": "nonce",
|
||||||
|
"type": "uint256"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name": "SignerAdded",
|
||||||
|
"type": "event"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"anonymous": false,
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"indexed": false,
|
||||||
|
"internalType": "address",
|
||||||
|
"name": "old_signer",
|
||||||
|
"type": "address"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"indexed": false,
|
||||||
|
"internalType": "uint256",
|
||||||
|
"name": "nonce",
|
||||||
|
"type": "uint256"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name": "SignerRemoved",
|
||||||
|
"type": "event"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"anonymous": false,
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"indexed": false,
|
||||||
|
"internalType": "uint16",
|
||||||
|
"name": "new_threshold",
|
||||||
|
"type": "uint16"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"indexed": false,
|
||||||
|
"internalType": "uint256",
|
||||||
|
"name": "nonce",
|
||||||
|
"type": "uint256"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name": "ThresholdSet",
|
||||||
|
"type": "event"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"internalType": "address",
|
||||||
|
"name": "new_signer",
|
||||||
|
"type": "address"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"internalType": "uint256",
|
||||||
|
"name": "nonce",
|
||||||
|
"type": "uint256"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"internalType": "bytes",
|
||||||
|
"name": "signatures",
|
||||||
|
"type": "bytes"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name": "add_signer",
|
||||||
|
"outputs": [],
|
||||||
|
"stateMutability": "nonpayable",
|
||||||
|
"type": "function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"internalType": "uint256",
|
||||||
|
"name": "nonce",
|
||||||
|
"type": "uint256"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"internalType": "bytes",
|
||||||
|
"name": "signatures",
|
||||||
|
"type": "bytes"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name": "burn_nonce",
|
||||||
|
"outputs": [],
|
||||||
|
"stateMutability": "nonpayable",
|
||||||
|
"type": "function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"inputs": [],
|
||||||
|
"name": "get_current_threshold",
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"internalType": "uint16",
|
||||||
|
"name": "",
|
||||||
|
"type": "uint16"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"stateMutability": "view",
|
||||||
|
"type": "function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"inputs": [],
|
||||||
|
"name": "get_valid_signer_count",
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"internalType": "uint8",
|
||||||
|
"name": "",
|
||||||
|
"type": "uint8"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"stateMutability": "view",
|
||||||
|
"type": "function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"internalType": "uint256",
|
||||||
|
"name": "nonce",
|
||||||
|
"type": "uint256"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name": "is_nonce_used",
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"internalType": "bool",
|
||||||
|
"name": "",
|
||||||
|
"type": "bool"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"stateMutability": "view",
|
||||||
|
"type": "function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"internalType": "address",
|
||||||
|
"name": "signer_address",
|
||||||
|
"type": "address"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name": "is_valid_signer",
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"internalType": "bool",
|
||||||
|
"name": "",
|
||||||
|
"type": "bool"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"stateMutability": "view",
|
||||||
|
"type": "function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"internalType": "address",
|
||||||
|
"name": "old_signer",
|
||||||
|
"type": "address"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"internalType": "uint256",
|
||||||
|
"name": "nonce",
|
||||||
|
"type": "uint256"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"internalType": "bytes",
|
||||||
|
"name": "signatures",
|
||||||
|
"type": "bytes"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name": "remove_signer",
|
||||||
|
"outputs": [],
|
||||||
|
"stateMutability": "nonpayable",
|
||||||
|
"type": "function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"internalType": "uint16",
|
||||||
|
"name": "new_threshold",
|
||||||
|
"type": "uint16"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"internalType": "uint256",
|
||||||
|
"name": "nonce",
|
||||||
|
"type": "uint256"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"internalType": "bytes",
|
||||||
|
"name": "signatures",
|
||||||
|
"type": "bytes"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name": "set_threshold",
|
||||||
|
"outputs": [],
|
||||||
|
"stateMutability": "nonpayable",
|
||||||
|
"type": "function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"internalType": "address",
|
||||||
|
"name": "",
|
||||||
|
"type": "address"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name": "signers",
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"internalType": "bool",
|
||||||
|
"name": "",
|
||||||
|
"type": "bool"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"stateMutability": "view",
|
||||||
|
"type": "function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"internalType": "bytes",
|
||||||
|
"name": "signatures",
|
||||||
|
"type": "bytes"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"internalType": "bytes",
|
||||||
|
"name": "message",
|
||||||
|
"type": "bytes"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"internalType": "uint256",
|
||||||
|
"name": "nonce",
|
||||||
|
"type": "uint256"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name": "verify_signatures",
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"internalType": "bool",
|
||||||
|
"name": "",
|
||||||
|
"type": "bool"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"stateMutability": "nonpayable",
|
||||||
|
"type": "function"
|
||||||
|
}
|
||||||
|
]
|
@ -6,3 +6,4 @@ export * from './staking-bridge';
|
|||||||
export * from './token-vesting';
|
export * from './token-vesting';
|
||||||
export * from './token';
|
export * from './token';
|
||||||
export * from './token-faucetable';
|
export * from './token-faucetable';
|
||||||
|
export * from './multisig-control';
|
||||||
|
55
libs/smart-contracts/src/contracts/multisig-control.ts
Normal file
55
libs/smart-contracts/src/contracts/multisig-control.ts
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
import { ethers } from 'ethers';
|
||||||
|
import abi from '../abis/multisig_abi.json';
|
||||||
|
|
||||||
|
export class MultisigControl {
|
||||||
|
public contract: ethers.Contract;
|
||||||
|
public address: string;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
address: string,
|
||||||
|
signerOrProvider: ethers.Signer | ethers.providers.Provider
|
||||||
|
) {
|
||||||
|
this.contract = new ethers.Contract(address, abi, signerOrProvider);
|
||||||
|
this.address = address;
|
||||||
|
}
|
||||||
|
|
||||||
|
add_signer(newSigner: string, nonce: string, signatures: string) {
|
||||||
|
return this.contract.add_signer(newSigner, nonce, signatures);
|
||||||
|
}
|
||||||
|
|
||||||
|
burn_nonce(nonce: string, signatures: string) {
|
||||||
|
return this.contract.burn_nonce(nonce, signatures);
|
||||||
|
}
|
||||||
|
|
||||||
|
get_current_threshold() {
|
||||||
|
return this.contract.get_current_threshold();
|
||||||
|
}
|
||||||
|
|
||||||
|
get_valid_signer_count() {
|
||||||
|
return this.contract.get_valid_signer_count();
|
||||||
|
}
|
||||||
|
|
||||||
|
is_nonce_used(nonce: string) {
|
||||||
|
return this.contract.is_nonce_used(nonce);
|
||||||
|
}
|
||||||
|
|
||||||
|
is_valid_signer(signerAddress: string) {
|
||||||
|
return this.contract.is_valid_signer(signerAddress);
|
||||||
|
}
|
||||||
|
|
||||||
|
remove_signer(oldSigner: string, nonce: string, signatures: string) {
|
||||||
|
return this.contract.remove_signer(oldSigner, nonce, signatures);
|
||||||
|
}
|
||||||
|
|
||||||
|
set_threshold(newThreshold: string, nonce: string, signatures: string) {
|
||||||
|
return this.contract.set_threshold(newThreshold, nonce, signatures);
|
||||||
|
}
|
||||||
|
|
||||||
|
signers(address: string) {
|
||||||
|
return this.contract.signers(address);
|
||||||
|
}
|
||||||
|
|
||||||
|
verify_signatures(nonce: string, message: string, signatures: string) {
|
||||||
|
return this.contract.verify_signatures(nonce, message, signatures);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,9 @@
|
|||||||
|
import { render } from '@testing-library/react';
|
||||||
|
import { BackgroundVideo } from './background-video';
|
||||||
|
|
||||||
|
describe('Background video', () => {
|
||||||
|
it('should render successfully', () => {
|
||||||
|
const { baseElement } = render(<BackgroundVideo />);
|
||||||
|
expect(baseElement).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,9 @@
|
|||||||
|
import type { Story, Meta } from '@storybook/react';
|
||||||
|
import { BackgroundVideo } from './background-video';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
component: BackgroundVideo,
|
||||||
|
title: 'BackgroundVideo',
|
||||||
|
} as Meta;
|
||||||
|
|
||||||
|
export const Default: Story = () => <BackgroundVideo />;
|
@ -1,4 +1,4 @@
|
|||||||
export const VegaBackgroundVideo = () => {
|
export const BackgroundVideo = () => {
|
||||||
return (
|
return (
|
||||||
<video
|
<video
|
||||||
autoPlay
|
autoPlay
|
1
libs/ui-toolkit/src/components/background-video/index.ts
Normal file
1
libs/ui-toolkit/src/components/background-video/index.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export * from './background-video';
|
@ -2,6 +2,7 @@ export * from './accordion';
|
|||||||
export * from './ag-grid';
|
export * from './ag-grid';
|
||||||
export * from './arrows';
|
export * from './arrows';
|
||||||
export * from './async-renderer';
|
export * from './async-renderer';
|
||||||
|
export * from './background-video';
|
||||||
export * from './button';
|
export * from './button';
|
||||||
export * from './callout';
|
export * from './callout';
|
||||||
export * from './checkbox';
|
export * from './checkbox';
|
||||||
|
@ -20,6 +20,7 @@
|
|||||||
"market-depth": "libs/market-depth",
|
"market-depth": "libs/market-depth",
|
||||||
"market-info": "libs/market-info",
|
"market-info": "libs/market-info",
|
||||||
"market-list": "libs/market-list",
|
"market-list": "libs/market-list",
|
||||||
|
"multisig-signer": "apps/multisig-signer",
|
||||||
"network-info": "libs/network-info",
|
"network-info": "libs/network-info",
|
||||||
"network-stats": "libs/network-stats",
|
"network-stats": "libs/network-stats",
|
||||||
"orders": "libs/orders",
|
"orders": "libs/orders",
|
||||||
|
Loading…
Reference in New Issue
Block a user