types#ChainType TypeScript Examples
The following examples show how to use
types#ChainType.
You can vote up the ones you like or vote down the ones you don't like,
and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: Account.ts From commonwealth with GNU General Public License v3.0 | 6 votes |
public async validate(signature: string) {
if (!this._validationToken) {
throw new Error('no validation token found');
}
if (!signature) {
throw new Error('signature required for validation');
}
const params : any = {
address: this.address,
chain: this.chain.id,
isToken: this.chain.type === ChainType.Token,
jwt: this.app.user.jwt,
signature,
wallet_id: this.walletId,
};
const result = await $.post(`${this.app.serverUrl()}/verifyAddress`, params);
if (result.status === 'Success') {
// update ghost address for discourse users
const hasGhostAddress = app.user.addresses.some(({ address, ghostAddress, chain }) => (
ghostAddress && this.chain.id === chain &&
app.user.activeAccounts.some((account) => account.address === address)
))
if (hasGhostAddress) {
const { success, ghostAddressId } = await $.post(`${this.app.serverUrl()}/updateAddress`, params);
if (success && ghostAddressId) {
// remove ghost address from addresses
app.user.setAddresses(app.user.addresses.filter(({ ghostAddress }) => {
return !ghostAddress
}));
app.user.setActiveAccounts([]);
}
}
}
}
Example #2
Source File: index.ts From contracts-ui with GNU General Public License v3.0 | 5 votes |
export function isResultReady(result: SubmittableResult, systemChainType: ChainType): boolean {
return systemChainType.isDevelopment ? result.isInBlock : result.isFinalized;
}
Example #3
Source File: index.ts From contracts-ui with GNU General Public License v3.0 | 5 votes |
export function getBlockHash(
status: SubmittableResult['status'],
systemChainType: ChainType
): Hash {
return systemChainType.isDevelopment ? status.asInBlock : status.asFinalized;
}
Example #4
Source File: app.ts From commonwealth with GNU General Public License v3.0 | 4 votes |
// called by the user, when clicking on the chain/node switcher menu
// returns a boolean reflecting whether initialization of chain via the
// initChain fn ought to proceed or abort
export async function selectChain(
chain?: ChainInfo,
deferred = false
): Promise<boolean> {
// Select the default node, if one wasn't provided
if (!chain) {
if (app.user.selectedChain) {
chain = app.user.selectedChain;
} else {
chain = app.config.chains.getById(app.config.defaultChain);
}
if (!chain) {
throw new Error('no chain available');
}
}
// Check for valid chain selection, and that we need to switch
if (app.chain && chain === app.chain.meta) {
return;
}
// Shut down old chain if applicable
await deinitChainOrCommunity();
app.chainPreloading = true;
document.title = `Commonwealth – ${chain.name}`;
setTimeout(() => m.redraw()); // redraw to show API status indicator
// Import top-level chain adapter lazily, to facilitate code split.
let newChain;
let initApi; // required for NEAR
if (chain.base === ChainBase.Substrate) {
const Substrate = (
await import(
/* webpackMode: "lazy" */
/* webpackChunkName: "substrate-main" */
'./controllers/chain/substrate/main'
)
).default;
newChain = new Substrate(chain, app);
} else if (chain.base === ChainBase.CosmosSDK) {
const Cosmos = (
await import(
/* webpackMode: "lazy" */
/* webpackChunkName: "cosmos-main" */
'./controllers/chain/cosmos/main'
)
).default;
newChain = new Cosmos(chain, app);
} else if (chain.network === ChainNetwork.Ethereum) {
const Ethereum = (
await import(
/* webpackMode: "lazy" */
/* webpackChunkName: "ethereum-main" */
'./controllers/chain/ethereum/main'
)
).default;
newChain = new Ethereum(chain, app);
} else if (
chain.network === ChainNetwork.NEAR ||
chain.network === ChainNetwork.NEARTestnet
) {
const Near = (
await import(
/* webpackMode: "lazy" */
/* webpackChunkName: "near-main" */
'./controllers/chain/near/main'
)
).default;
newChain = new Near(chain, app);
initApi = true;
} else if (chain.network === ChainNetwork.Sputnik) {
const Sputnik = (
await import(
/* webpackMode: "lazy" */
/* webpackChunkName: "sputnik-main" */
'./controllers/chain/near/sputnik/adapter'
)
).default;
newChain = new Sputnik(chain, app);
initApi = true;
} else if (chain.network === ChainNetwork.Moloch) {
const Moloch = (
await import(
/* webpackMode: "lazy" */
/* webpackChunkName: "moloch-main" */
'./controllers/chain/ethereum/moloch/adapter'
)
).default;
newChain = new Moloch(chain, app);
} else if (chain.network === ChainNetwork.Compound) {
const Compound = (
await import(
/* webpackMode: "lazy" */
/* webpackChunkName: "compound-main" */
'./controllers/chain/ethereum/compound/adapter'
)
).default;
newChain = new Compound(chain, app);
} else if (chain.network === ChainNetwork.Aave) {
const Aave = (
await import(
/* webpackMode: "lazy" */
/* webpackChunkName: "aave-main" */
'./controllers/chain/ethereum/aave/adapter'
)
).default;
newChain = new Aave(chain, app);
} else if (
chain.network === ChainNetwork.ERC20 ||
chain.network === ChainNetwork.AxieInfinity
) {
const ERC20 = (
await import(
// /* webpackMode: "lazy" */
// /* webpackChunkName: "erc20-main" */
'./controllers/chain/ethereum/tokenAdapter'
)
).default;
newChain = new ERC20(chain, app);
} else if (chain.network === ChainNetwork.ERC721) {
const ERC721 = (
await import(
// /* webpackMode: "lazy" */
// /* webpackChunkName: "erc721-main" */
'./controllers/chain/ethereum/NftAdapter'
)
).default;
newChain = new ERC721(chain, app);
} else if (chain.network === ChainNetwork.SPL) {
const SPL = (
await import(
// /* webpackMode: "lazy" */
// /* webpackChunkName: "spl-main" */
'./controllers/chain/solana/tokenAdapter'
)
).default;
newChain = new SPL(chain, app);
} else if (chain.base === ChainBase.Solana) {
const Solana = (
await import(
/* webpackMode: "lazy" */
/* webpackChunkName: "solana-main" */
'./controllers/chain/solana/main'
)
).default;
newChain = new Solana(chain, app);
} else if (chain.network === ChainNetwork.Commonwealth) {
const Commonwealth = (
await import(
/* webpackMode: "lazy" */
/* webpackChunkName: "commonwealth-main" */
'./controllers/chain/ethereum/commonwealth/adapter'
)
).default;
newChain = new Commonwealth(chain, app);
} else if (
chain.base === ChainBase.Ethereum &&
chain.type === ChainType.Offchain
) {
const Ethereum = (
await import(
/* webpackMode: "lazy" */
/* webpackChunkName: "ethereum-main" */
'./controllers/chain/ethereum/main'
)
).default;
newChain = new Ethereum(chain, app);
} else {
throw new Error('Invalid chain');
}
// Load server data without initializing modules/chain connection.
const finalizeInitialization = await newChain.initServer();
// If the user is still on the initializing node, finalize the
// initialization; otherwise, abort, deinit, and return false.
//
// Also make sure the state is sufficiently reset so that the
// next redraw cycle will reinitialize any needed chain.
if (!finalizeInitialization) {
console.log('Chain loading aborted');
app.chainPreloading = false;
app.chain = null;
return false;
} else {
app.chain = newChain;
}
if (initApi) {
await app.chain.initApi(); // required for loading NearAccounts
}
app.chainPreloading = false;
app.chain.deferred = deferred;
// Instantiate active addresses before chain fully loads
await updateActiveAddresses(chain);
// Update default on server if logged in
if (app.isLoggedIn()) {
await app.user.selectChain({
chain: chain.id,
});
}
// If the user was invited to a chain/community, we can now pop up a dialog for them to accept the invite
handleInviteLinkRedirect();
// Redraw with not-yet-loaded chain and return true to indicate
// initialization has finalized.
m.redraw();
return true;
}
Example #5
Source File: app.ts From commonwealth with GNU General Public License v3.0 | 4 votes |
Promise.all([$.ready, $.get('/api/domain')]).then(
async ([ready, { customDomain }]) => {
// set window error handler
// ignore ResizeObserver error: https://stackoverflow.com/questions/49384120/resizeobserver-loop-limit-exceeded
const resizeObserverLoopErrRe = /^ResizeObserver loop limit exceeded/;
// replace chunk loading errors with a notification that the app has been updated
const chunkLoadingErrRe = /^Uncaught SyntaxError: Unexpected token/;
window.onerror = (errorMsg, url, lineNumber, colNumber, error) => {
if (
typeof errorMsg === 'string' &&
resizeObserverLoopErrRe.test(errorMsg)
)
return false;
if (typeof errorMsg === 'string' && chunkLoadingErrRe.test(errorMsg)) {
alertModalWithText(
APPLICATION_UPDATE_MESSAGE,
APPLICATION_UPDATE_ACTION
)();
return false;
}
notifyError(`${errorMsg}`);
return false;
};
const redirectRoute = (path: string | Function) => ({
render: (vnode) => {
m.route.set(
typeof path === 'string' ? path : path(vnode.attrs),
{},
{ replace: true }
);
return m(Layout);
},
});
interface RouteAttrs {
scoped: string | boolean;
hideSidebar?: boolean;
deferChain?: boolean;
}
let hasCompletedSuccessfulPageLoad = false;
const importRoute = (path: string, attrs: RouteAttrs) => ({
onmatch: async () => {
return import(
/* webpackMode: "lazy" */
/* webpackChunkName: "route-[request]" */
`./${path}`
)
.then((p) => {
hasCompletedSuccessfulPageLoad = true;
return p.default;
})
.catch((err) => {
// handle import() error
console.error(err);
if (err.name === 'ChunkLoadError') {
alertModalWithText(
APPLICATION_UPDATE_MESSAGE,
APPLICATION_UPDATE_ACTION
)();
}
// return to the last page, if it was on commonwealth
// eslint-disable-next-line no-restricted-globals
if (hasCompletedSuccessfulPageLoad) history.back();
});
},
render: (vnode) => {
const { scoped, hideSidebar } = attrs;
const scope =
typeof scoped === 'string'
? // string => scope is defined by route
scoped
: scoped
? // true => scope is derived from path
vnode.attrs.scope?.toString() || customDomain
: // false => scope is null
null;
if (scope) {
const scopeIsEthereumAddress =
scope.startsWith('0x') && scope.length === 42;
if (scopeIsEthereumAddress) {
const chains = app.config.chains.getAll();
const chain = chains.find((o) => o.address === scope);
if (chain) {
const pagePath = window.location.href.substr(
window.location.href.indexOf(scope) + scope.length
);
m.route.set(`/${chain.id}${pagePath}`);
}
}
}
// Special case to defer chain loading specifically for viewing an offchain thread. We need
// a special case because OffchainThreads and on-chain proposals are all viewed through the
// same "/:scope/proposal/:type/:id" route.
let deferChain = attrs.deferChain;
const isDiscussion =
vnode.attrs.type === 'discussion' ||
pathIsDiscussion(scope, window.location.pathname);
if (path === 'views/pages/view_proposal/index' && isDiscussion) {
deferChain = true;
}
if (app.chain?.meta.type === ChainType.Token) {
deferChain = false;
}
return m(Layout, { scope, deferChain, hideSidebar }, [vnode]);
},
});
const isCustomDomain = !!customDomain;
const { activeAccount } = app.user;
m.route(document.body, '/', {
// Sitewide pages
'/about': importRoute('views/pages/commonwealth', {
scoped: false,
}),
'/terms': importRoute('views/pages/landing/terms', { scoped: false }),
'/privacy': importRoute('views/pages/landing/privacy', { scoped: false }),
'/components': importRoute('views/pages/components', {
scoped: false,
hideSidebar: true,
}),
'/createCommunity': importRoute('views/pages/create_community', {
scoped: false,
}),
...(isCustomDomain
? {
//
// Custom domain routes
//
'/': importRoute('views/pages/discussions', {
scoped: true,
deferChain: true,
}),
'/search': importRoute('views/pages/search', {
scoped: false,
deferChain: true,
}),
// Notifications
'/notification-settings': importRoute(
'views/pages/notification_settings',
{ scoped: true, deferChain: true }
),
'/notifications': importRoute('views/pages/notifications_page', {
scoped: true,
deferChain: true,
}),
// CMN
'/projects': importRoute('views/pages/commonwealth/projects', {
scoped: true,
}),
'/backers': importRoute('views/pages/commonwealth/backers', {
scoped: true,
}),
'/collectives': importRoute(
'views/pages/commonwealth/collectives',
{ scoped: true }
),
// NEAR
'/finishNearLogin': importRoute('views/pages/finish_near_login', {
scoped: true,
}),
'/finishaxielogin': importRoute('views/pages/finish_axie_login', {
scoped: true,
}),
// Discussions
'/home': redirectRoute((attrs) => `/${attrs.scope}/`),
'/discussions': redirectRoute((attrs) => `/${attrs.scope}/`),
'/discussions/:topic': importRoute('views/pages/discussions', {
scoped: true,
deferChain: true,
}),
'/members': importRoute('views/pages/members', {
scoped: true,
deferChain: true,
}),
'/sputnik-daos': importRoute('views/pages/sputnikdaos', {
scoped: true,
deferChain: true,
}),
'/chat/:channel': importRoute('views/pages/chat', {
scoped: true,
deferChain: true,
}),
'/new/discussion': importRoute('views/pages/new_thread', {
scoped: true,
deferChain: true,
}),
// Profiles
'/account/:address': importRoute('views/pages/profile', {
scoped: true,
deferChain: true,
}),
'/account': redirectRoute((a) =>
activeAccount ? `/account/${activeAccount.address}` : '/'
),
// Governance
'/referenda': importRoute('views/pages/referenda', {
scoped: true,
}),
'/proposals': importRoute('views/pages/proposals', {
scoped: true,
}),
'/council': importRoute('views/pages/council', { scoped: true }),
'/delegate': importRoute('views/pages/delegate', { scoped: true }),
'/proposal/:type/:identifier': importRoute(
'views/pages/view_proposal/index',
{ scoped: true }
),
'/proposal/:identifier': importRoute(
'views/pages/view_proposal/index',
{ scoped: true }
),
'/discussion/:identifier': importRoute(
'views/pages/view_proposal/index',
{ scoped: true }
),
'/new/proposal/:type': importRoute(
'views/pages/new_proposal/index',
{ scoped: true }
),
'/new/proposal': importRoute('views/pages/new_proposal/index', {
scoped: true,
}),
// Treasury
'/treasury': importRoute('views/pages/treasury', { scoped: true }),
'/bounties': importRoute('views/pages/bounties', { scoped: true }),
'/tips': importRoute('views/pages/tips', { scoped: true }),
'/validators': importRoute('views/pages/validators', {
scoped: true,
}),
// Settings
'/login': importRoute('views/pages/login', {
scoped: true,
deferChain: true,
}),
'/web3login': importRoute('views/pages/web3login', {
scoped: true,
deferChain: true,
}),
// Admin
'/admin': importRoute('views/pages/admin', { scoped: true }),
'/manage': importRoute('views/pages/manage_community/index', {
scoped: true,
}),
'/spec_settings': importRoute('views/pages/spec_settings', {
scoped: true,
deferChain: true,
}),
'/settings': importRoute('views/pages/settings', { scoped: true }),
'/analytics': importRoute('views/pages/stats', {
scoped: true,
deferChain: true,
}),
'/snapshot/:snapshotId': importRoute(
'views/pages/snapshot_proposals',
{ scoped: true, deferChain: true }
),
'/multiple-snapshots': importRoute(
'views/pages/view_multiple_snapshot_spaces',
{ scoped: true, deferChain: true }
),
'/snapshot/:snapshotId/:identifier': importRoute(
'views/pages/view_snapshot_proposal',
{ scoped: true, deferChain: true }
),
'/new/snapshot/:snapshotId': importRoute(
'views/pages/new_snapshot_proposal',
{ scoped: true, deferChain: true }
),
// Redirects
'/:scope/dashboard': redirectRoute(() => '/'),
'/:scope/notifications': redirectRoute(() => '/notifications'),
'/:scope/notification-settings': redirectRoute(
() => '/notification-settings'
),
'/:scope/projects': redirectRoute(() => '/projects'),
'/:scope/backers': redirectRoute(() => '/backers'),
'/:scope/collectives': redirectRoute(() => '/collectives'),
'/:scope/finishNearLogin': redirectRoute(() => '/finishNearLogin'),
'/:scope/finishaxielogin': redirectRoute(() => '/finishaxielogin'),
'/:scope/home': redirectRoute(() => '/'),
'/:scope/discussions': redirectRoute(() => '/'),
'/:scope': redirectRoute(() => '/'),
'/:scope/discussions/:topic': redirectRoute(
(attrs) => `/discussions/${attrs.topic}/`
),
'/:scope/search': redirectRoute(() => '/search'),
'/:scope/members': redirectRoute(() => '/members'),
'/:scope/sputnik-daos': redirectRoute(() => '/sputnik-daos'),
'/:scope/chat/:channel': redirectRoute(
(attrs) => `/chat/${attrs.channel}`
),
'/:scope/new/discussion': redirectRoute(() => '/new/discussion'),
'/:scope/account/:address': redirectRoute(
(attrs) => `/account/${attrs.address}/`
),
'/:scope/account': redirectRoute(() =>
activeAccount ? `/account/${activeAccount.address}` : '/'
),
'/:scope/referenda': redirectRoute(() => '/referenda'),
'/:scope/proposals': redirectRoute(() => '/proposals'),
'/:scope/council': redirectRoute(() => '/council'),
'/:scope/delegate': redirectRoute(() => '/delegate'),
'/:scope/proposal/:type/:identifier': redirectRoute(
(attrs) => `/proposal/${attrs.type}/${attrs.identifier}/`
),
'/:scope/proposal/:identifier': redirectRoute(
(attrs) => `/proposal/${attrs.identifier}/`
),
'/:scope/discussion/:identifier': redirectRoute(
(attrs) => `/discussion/${attrs.identifier}/`
),
'/:scope/new/proposal/:type': redirectRoute(
(attrs) => `/new/proposal/${attrs.type}/`
),
'/:scope/new/proposal': redirectRoute(() => '/new/proposal'),
'/:scope/treasury': redirectRoute(() => '/treasury'),
'/:scope/bounties': redirectRoute(() => '/bounties'),
'/:scope/tips': redirectRoute(() => '/tips'),
'/:scope/validators': redirectRoute(() => '/validators'),
'/:scope/login': redirectRoute(() => '/login'),
'/:scope/web3login': redirectRoute(() => '/web3login'),
'/:scope/settings': redirectRoute(() => '/settings'),
'/:scope/admin': redirectRoute(() => '/admin'),
'/:scope/manage': redirectRoute(() => '/manage'),
'/:scope/spec_settings': redirectRoute(() => '/spec_settings'),
'/:scope/analytics': redirectRoute(() => '/analytics'),
'/:scope/snapshot-proposals/:snapshotId': redirectRoute(
(attrs) => `/snapshot/${attrs.snapshotId}`
),
'/:scope/snapshot-proposal/:snapshotId/:identifier': redirectRoute(
(attrs) => `/snapshot/${attrs.snapshotId}/${attrs.identifier}`
),
'/:scope/new/snapshot-proposal/:snapshotId': redirectRoute(
(attrs) => `/new/snapshot/${attrs.snapshotId}`
),
'/:scope/snapshot-proposals/:snapshotId/:identifier': redirectRoute(
(attrs) => `/snapshot/${attrs.snapshotId}/${attrs.identifier}`
),
'/:scope/new/snapshot-proposals/:snapshotId': redirectRoute(
(attrs) => `/new/snapshot/${attrs.snapshotId}`
),
}
: {
//
// Global routes
//
'/': importRoute('views/pages/landing', {
scoped: false,
hideSidebar: false,
}),
'/communities': importRoute('views/pages/community_cards', {
scoped: false,
hideSidebar: false,
}),
'/search': importRoute('views/pages/search', {
scoped: false,
deferChain: true,
}),
'/whyCommonwealth': importRoute('views/pages/commonwealth', {
scoped: false,
hideSidebar: true,
}),
'/dashboard': importRoute('views/pages/user_dashboard', {
scoped: false,
deferChain: true,
}),
'/dashboard/:type': importRoute('views/pages/user_dashboard', {
scoped: false,
deferChain: true,
}),
// Scoped routes
//
// Notifications
'/:scope/notifications': importRoute(
'views/pages/notifications_page',
{ scoped: true, deferChain: true }
),
'/notifications': redirectRoute(() => '/edgeware/notifications'),
'/:scope/notification-settings': importRoute(
'views/pages/notification_settings',
{ scoped: true, deferChain: true }
),
'/notification-settings': redirectRoute(
() => '/edgeware/notification-settings'
),
// CMN
'/:scope/projects': importRoute(
'views/pages/commonwealth/projects',
{ scoped: true }
),
'/:scope/backers': importRoute('views/pages/commonwealth/backers', {
scoped: true,
}),
'/:scope/collectives': importRoute(
'views/pages/commonwealth/collectives',
{ scoped: true }
),
// NEAR
'/:scope/finishNearLogin': importRoute(
'views/pages/finish_near_login',
{ scoped: true }
),
'/finishaxielogin': importRoute('views/pages/finish_axie_login', {
scoped: false,
}),
// Settings
'/settings': redirectRoute(() => '/edgeware/settings'),
'/:scope/settings': importRoute('views/pages/settings', {
scoped: true,
}),
// Discussions
'/home': redirectRoute('/'), // legacy redirect, here for compatibility only
'/discussions': redirectRoute('/'), // legacy redirect, here for compatibility only
'/:scope/home': redirectRoute((attrs) => `/${attrs.scope}/`),
'/:scope/discussions': redirectRoute((attrs) => `/${attrs.scope}/`),
'/:scope': importRoute('views/pages/discussions', {
scoped: true,
deferChain: true,
}),
'/:scope/discussions/:topic': importRoute(
'views/pages/discussions',
{ scoped: true, deferChain: true }
),
'/:scope/search': importRoute('views/pages/search', {
scoped: true,
deferChain: true,
}),
'/:scope/members': importRoute('views/pages/members', {
scoped: true,
deferChain: true,
}),
'/:scope/sputnik-daos': importRoute('views/pages/sputnikdaos', {
scoped: true,
deferChain: true,
}),
'/:scope/chat/:channel': importRoute('views/pages/chat', {
scoped: true,
deferChain: true,
}),
'/:scope/new/discussion': importRoute('views/pages/new_thread', {
scoped: true,
deferChain: true,
}),
// Profiles
'/:scope/account/:address': importRoute('views/pages/profile', {
scoped: true,
deferChain: true,
}),
'/:scope/account': redirectRoute((a) =>
activeAccount
? `/${a.scope}/account/${activeAccount.address}`
: `/${a.scope}/`
),
// Governance
'/:scope/referenda': importRoute('views/pages/referenda', {
scoped: true,
}),
'/:scope/proposals': importRoute('views/pages/proposals', {
scoped: true,
}),
'/:scope/council': importRoute('views/pages/council', {
scoped: true,
}),
'/:scope/delegate': importRoute('views/pages/delegate', {
scoped: true,
}),
'/:scope/proposal/:type/:identifier': importRoute(
'views/pages/view_proposal/index',
{ scoped: true }
),
'/:scope/proposal/:identifier': importRoute(
'views/pages/view_proposal/index',
{ scoped: true }
),
'/:scope/discussion/:identifier': importRoute(
'views/pages/view_proposal/index',
{ scoped: true }
),
'/:scope/new/proposal/:type': importRoute(
'views/pages/new_proposal/index',
{ scoped: true }
),
'/:scope/new/proposal': importRoute(
'views/pages/new_proposal/index',
{ scoped: true }
),
// Treasury
'/:scope/treasury': importRoute('views/pages/treasury', {
scoped: true,
}),
'/:scope/bounties': importRoute('views/pages/bounties', {
scoped: true,
}),
'/:scope/tips': importRoute('views/pages/tips', { scoped: true }),
'/:scope/validators': importRoute('views/pages/validators', {
scoped: true,
}),
// Settings
'/login': importRoute('views/pages/login', { scoped: false }),
'/:scope/login': importRoute('views/pages/login', {
scoped: true,
deferChain: true,
}),
'/:scope/web3login': importRoute('views/pages/web3login', {
scoped: true,
deferChain: true,
}),
// Admin
'/:scope/admin': importRoute('views/pages/admin', { scoped: true }),
'/manage': importRoute('views/pages/manage_community/index', {
scoped: false,
}),
'/:scope/manage': importRoute(
'views/pages/manage_community/index',
{ scoped: true }
),
'/:scope/spec_settings': importRoute('views/pages/spec_settings', {
scoped: true,
deferChain: true,
}),
'/:scope/analytics': importRoute('views/pages/stats', {
scoped: true,
deferChain: true,
}),
'/:scope/snapshot/:snapshotId': importRoute(
'views/pages/snapshot_proposals',
{ scoped: true, deferChain: true }
),
'/:scope/multiple-snapshots': importRoute(
'views/pages/view_multiple_snapshot_spaces',
{ scoped: true, deferChain: true }
),
'/:scope/snapshot/:snapshotId/:identifier': importRoute(
'views/pages/view_snapshot_proposal',
{ scoped: true, deferChain: true }
),
'/:scope/new/snapshot/:snapshotId': importRoute(
'views/pages/new_snapshot_proposal',
{ scoped: true, deferChain: true }
),
'/:scope/snapshot-proposals/:snapshotId': redirectRoute(
(attrs) => `/${attrs.scope}/snapshot/${attrs.snapshotId}`
),
'/:scope/snapshot-proposal/:snapshotId/:identifier': redirectRoute(
(attrs) =>
`/${attrs.scope}/snapshot/${attrs.snapshotId}/${attrs.identifier}`
),
'/:scope/new/snapshot-proposal/:snapshotId': redirectRoute(
(attrs) => `/${attrs.scope}/new/snapshot/${attrs.snapshotId}`
),
'/:scope/snapshot-proposals/:snapshotId/:identifier': redirectRoute(
(attrs) =>
`/${attrs.scope}/snapshot/${attrs.snapshotId}/${attrs.identifier}`
),
'/:scope/new/snapshot-proposals/:snapshotId': redirectRoute(
(attrs) => `/${attrs.scope}/new/snapshot/${attrs.snapshotId}`
),
}),
});
const script = document.createElement('noscript');
// eslint-disable-next-line max-len
m.render(
script,
m.trust(
'<iframe src="https://www.googletagmanager.com/ns.html?id=GTM-KRWH69V" height="0" width="0" style="display:none;visibility:hidden"></iframe>'
)
);
document.body.insertBefore(script, document.body.firstChild);
// initialize construct-ui focus manager
FocusManager.showFocusOnlyOnTab();
// initialize mixpanel, before adding an alias or tracking identity
try {
if (
document.location.host.startsWith('localhost') ||
document.location.host.startsWith('127.0.0.1')
) {
mixpanel.init(MIXPANEL_DEV_TOKEN, { debug: true });
} else {
// Production Mixpanel Project
mixpanel.init(MIXPANEL_PROD_TOKEN, { debug: true });
}
} catch (e) {
console.error('Mixpanel initialization error');
}
// handle login redirects
if (
m.route.param('loggedin') &&
m.route.param('loggedin').toString() === 'true' &&
m.route.param('path') &&
!m.route.param('path').startsWith('/login')
) {
// (we call toString() because m.route.param() returns booleans, even though the types don't reflect this)
// handle param-based redirect after email login
/* If we are creating a new account, then we alias to create a new mixpanel user
else we identify to associate mixpanel events
*/
if (m.route.param('new') && m.route.param('new').toString() === 'true') {
console.log('creating account');
try {
} catch (err) {
// Don't do anything... Just identify if there is an error
// mixpanel.identify(m.route.param('email').toString());
}
} else {
}
m.route.set(m.route.param('path'), {}, { replace: true });
} else if (
localStorage &&
localStorage.getItem &&
localStorage.getItem('githubPostAuthRedirect')
) {
// handle localStorage-based redirect after Github login (callback must occur within 30 seconds)
try {
const postAuth = JSON.parse(
localStorage.getItem('githubPostAuthRedirect')
);
if (postAuth.path && +new Date() - postAuth.timestamp < 30 * 1000) {
m.route.set(postAuth.path, {}, { replace: true });
}
localStorage.removeItem('githubPostAuthRedirect');
} catch (e) {
console.log('Error restoring path from localStorage');
}
} else if (
localStorage &&
localStorage.getItem &&
localStorage.getItem('discordPostAuthRedirect')
) {
try {
const postAuth = JSON.parse(
localStorage.getItem('discordPostAuthRedirect')
);
if (postAuth.path && +new Date() - postAuth.timestamp < 30 * 1000) {
m.route.set(postAuth.path, {}, { replace: true });
}
localStorage.removeItem('discordPostAuthRedirect');
} catch (e) {
console.log('Error restoring path from localStorage');
}
}
if (m.route.param('loggedin')) {
notifySuccess('Logged in!');
} else if (m.route.param('loginerror')) {
notifyError('Could not log in');
console.error(m.route.param('loginerror'));
}
// initialize the app
initAppState(true, customDomain)
.then(async () => {
if (app.loginState === LoginState.LoggedIn) {
// refresh notifications once
app.user.notifications.refresh().then(() => m.redraw());
// grab all discussion drafts
app.user.discussionDrafts.refreshAll().then(() => m.redraw());
}
handleInviteLinkRedirect();
// If the user updates their email
handleUpdateEmailConfirmation();
m.redraw();
})
.catch(() => {
m.redraw();
});
}
);
Example #6
Source File: governance_section.tsx From commonwealth with GNU General Public License v3.0 | 4 votes |
view(vnode) {
// Conditional Render Details
const hasProposals =
app.chain &&
(app.chain.base === ChainBase.CosmosSDK ||
app.chain.network === ChainNetwork.Sputnik ||
(app.chain.base === ChainBase.Substrate &&
app.chain.network !== ChainNetwork.Plasm) ||
app.chain.network === ChainNetwork.Moloch ||
app.chain.network === ChainNetwork.Compound ||
app.chain.network === ChainNetwork.Aave ||
app.chain.network === ChainNetwork.Commonwealth ||
app.chain.meta.snapshot);
if (!hasProposals) return;
const isNotOffchain = app.chain?.meta.type !== ChainType.Offchain;
const showMolochMenuOptions =
isNotOffchain &&
app.user.activeAccount &&
app.chain?.network === ChainNetwork.Moloch;
const showMolochMemberOptions =
isNotOffchain &&
showMolochMenuOptions &&
(app.user.activeAccount as any)?.shares?.gtn(0);
const showCommonwealthMenuOptions =
isNotOffchain && app.chain?.network === ChainNetwork.Commonwealth;
const showCompoundOptions =
isNotOffchain &&
app.user.activeAccount &&
app.chain?.network === ChainNetwork.Compound;
const showAaveOptions =
isNotOffchain &&
app.user.activeAccount &&
app.chain?.network === ChainNetwork.Aave;
const showSnapshotOptions =
isNotOffchain && app.chain?.meta.snapshot?.length > 0;
const showReferenda =
isNotOffchain &&
app.chain?.base === ChainBase.Substrate &&
app.chain.network !== ChainNetwork.Darwinia &&
app.chain.network !== ChainNetwork.HydraDX;
const showProposals =
isNotOffchain &&
((app.chain?.base === ChainBase.Substrate &&
app.chain.network !== ChainNetwork.Darwinia) ||
(app.chain?.base === ChainBase.CosmosSDK &&
app.chain.network !== ChainNetwork.Terra) ||
app.chain?.network === ChainNetwork.Sputnik ||
app.chain?.network === ChainNetwork.Moloch ||
app.chain?.network === ChainNetwork.Compound ||
app.chain?.network === ChainNetwork.Aave);
const showCouncillors =
isNotOffchain && app.chain?.base === ChainBase.Substrate;
const showTreasury =
isNotOffchain &&
app.chain?.base === ChainBase.Substrate &&
app.chain.network !== ChainNetwork.Centrifuge;
const showBounties =
isNotOffchain &&
app.chain?.base === ChainBase.Substrate &&
app.chain.network !== ChainNetwork.Centrifuge &&
app.chain.network !== ChainNetwork.HydraDX;
const showTips =
isNotOffchain &&
app.chain?.base === ChainBase.Substrate &&
app.chain.network !== ChainNetwork.Centrifuge;
const showValidators =
isNotOffchain &&
app.chain?.base === ChainBase.Substrate &&
app.chain?.network !== ChainNetwork.Kulupu &&
app.chain?.network !== ChainNetwork.Darwinia;
// ---------- Build Toggle Tree ---------- //
const governanceDefaultToggleTree: ToggleTree = {
toggledState: true,
children: {
Members: {
toggledState: false,
children: {},
},
...(showSnapshotOptions && {
Snapshots: {
toggledState: false,
children: {},
},
}),
...(showCompoundOptions && {
Delegate: {
toggledState: true,
children: {},
},
}),
...(showTreasury && {
Treasury: {
toggledState: false,
children: {},
},
}),
...(showBounties && {
Bounties: {
toggledState: false,
children: {},
},
}),
...(showReferenda && {
Referenda: {
toggledState: false,
children: {},
},
}),
...(showProposals && {
Proposals: {
toggledState: false,
children: {},
},
}),
...(showTips && {
Tips: {
toggledState: false,
children: {},
},
}),
...(showCouncillors && {
Councillors: {
toggledState: false,
children: {},
},
}),
...(showValidators && {
Validators: {
toggledState: false,
children: {},
},
}),
},
};
// Check if an existing toggle tree is stored
if (!localStorage[`${app.activeChainId()}-governance-toggle-tree`]) {
console.log('setting toggle tree from scratch');
localStorage[`${app.activeChainId()}-governance-toggle-tree`] =
JSON.stringify(governanceDefaultToggleTree);
} else if (
!verifyCachedToggleTree('governance', governanceDefaultToggleTree)
) {
console.log(
'setting discussions toggle tree since the cached version differs from the updated version'
);
localStorage[`${app.activeChainId()}-governance-toggle-tree`] =
JSON.stringify(governanceDefaultToggleTree);
}
let toggleTreeState = JSON.parse(
localStorage[`${app.activeChainId()}-governance-toggle-tree`]
);
if (vnode.attrs.mobile) {
toggleTreeState = governanceDefaultToggleTree;
}
const onSnapshotProposal = (p) =>
p.startsWith(`/${app.activeChainId()}/snapshot`);
const onSnapshotProposalCreation = (p) =>
p.startsWith(`/${app.activeChainId()}/new/snapshot/`);
const onProposalPage = (p) =>
p.startsWith(`/${app.activeChainId()}/proposals`) ||
p.startsWith(
`/${app.activeChainId()}/proposal/${
ProposalType.SubstrateDemocracyProposal
}`
);
const onReferendaPage = (p) =>
p.startsWith(`/${app.activeChainId()}/referenda`) ||
p.startsWith(
`/${app.activeChainId()}/proposal/${
ProposalType.SubstrateDemocracyReferendum
}`
);
const onTreasuryPage = (p) =>
p.startsWith(`/${app.activeChainId()}/treasury`) ||
p.startsWith(
`/${app.activeChainId()}/proposal/${
ProposalType.SubstrateTreasuryProposal
}`
);
const onBountiesPage = (p) =>
p.startsWith(`/${app.activeChainId()}/bounties`);
const onTipsPage = (p) =>
p.startsWith(`/${app.activeChainId()}/tips`) ||
p.startsWith(
`/${app.activeChainId()}/proposal/${ProposalType.SubstrateTreasuryTip}`
);
const onCouncilPage = (p) =>
p.startsWith(`/${app.activeChainId()}/council`);
const onMotionPage = (p) =>
p.startsWith(`/${app.activeChainId()}/motions`) ||
p.startsWith(
`/${app.activeChainId()}/proposal/${
ProposalType.SubstrateCollectiveProposal
}`
);
const onValidatorsPage = (p) =>
p.startsWith(`/${app.activeChainId()}/validators`);
const onNotificationsPage = (p) => p.startsWith('/notifications');
const onMembersPage = (p) =>
p.startsWith(`/${app.activeChainId()}/members`) ||
p.startsWith(`/${app.activeChainId()}/account/`);
if (onNotificationsPage(m.route.get())) return;
// ---------- Build Section Props ---------- //
// Members
const membersData: SectionGroupAttrs = {
title: 'Members',
containsChildren: false,
hasDefaultToggle: toggleTreeState['children']['Members']['toggledState'],
isVisible: true,
isUpdated: true,
isActive:
onMembersPage(m.route.get()) &&
(app.chain ? app.chain.serverLoaded : true),
onclick: (e, toggle: boolean) => {
e.preventDefault();
setGovernanceToggleTree('children.Members.toggledState', toggle);
navigateToSubpage('/members');
},
displayData: null,
};
// Snapshots
const snapshotData: SectionGroupAttrs = {
title: 'Snapshots',
containsChildren: false,
hasDefaultToggle: showSnapshotOptions
? toggleTreeState['children']['Snapshots']['toggledState']
: false,
isVisible: showSnapshotOptions,
isActive: onSnapshotProposal(m.route.get()),
isUpdated: true,
onclick: (e, toggle: boolean) => {
e.preventDefault();
setGovernanceToggleTree('children.Snapshots.toggledState', toggle);
// Check if we have multiple snapshots for conditional redirect
const snapshotSpaces = app.chain.meta.snapshot;
if (snapshotSpaces.length > 1) {
navigateToSubpage('/multiple-snapshots', { action: 'select-space' });
} else {
navigateToSubpage(`/snapshot/${snapshotSpaces}`);
}
},
displayData: null,
};
// Proposals
const proposalsData: SectionGroupAttrs = {
title: 'Proposals',
containsChildren: false,
hasDefaultToggle: showProposals
? toggleTreeState['children']['Proposals']['toggledState']
: false,
onclick: (e, toggle: boolean) => {
e.preventDefault();
navigateToSubpage('/proposals');
setGovernanceToggleTree('children.Proposals.toggledState', toggle);
},
isVisible: showProposals,
isUpdated: true,
isActive: onProposalPage(m.route.get()),
displayData: null,
};
// Treasury
const treasuryData: SectionGroupAttrs = {
title: 'Treasury',
containsChildren: false,
hasDefaultToggle: showTreasury
? toggleTreeState['children']['Treasury']['toggledState']
: false,
onclick: (e, toggle: boolean) => {
e.preventDefault();
navigateToSubpage('/treasury');
setGovernanceToggleTree('children.Treasury.toggledState', toggle);
},
isVisible: showTreasury,
isUpdated: true,
isActive: onTreasuryPage(m.route.get()),
displayData: null,
};
const bountyData: SectionGroupAttrs = {
title: 'Bounties',
containsChildren: false,
hasDefaultToggle: showBounties
? toggleTreeState['children']['Bounties']['toggledState']
: false,
onclick: (e, toggle: boolean) => {
e.preventDefault();
navigateToSubpage('/bounties');
setGovernanceToggleTree('children.Bounties.toggledState', toggle);
},
isVisible: showBounties,
isUpdated: true,
isActive: onBountiesPage(m.route.get()),
displayData: null,
};
const referendaData: SectionGroupAttrs = {
title: 'Referenda',
containsChildren: false,
hasDefaultToggle: showReferenda
? toggleTreeState['children']['Referenda']['toggledState']
: false,
onclick: (e, toggle: boolean) => {
e.preventDefault();
navigateToSubpage('/referenda');
setGovernanceToggleTree('children.Referenda.toggledState', toggle);
},
isVisible: showReferenda,
isUpdated: true,
isActive: onReferendaPage(m.route.get()),
displayData: null,
};
const tipsData: SectionGroupAttrs = {
title: 'Tips',
containsChildren: false,
hasDefaultToggle: showTips
? toggleTreeState['children']['Tips']['toggledState']
: false,
onclick: (e, toggle: boolean) => {
e.preventDefault();
navigateToSubpage('/tips');
setGovernanceToggleTree('children.Tips.toggledState', toggle);
},
isVisible: showTips,
isUpdated: true,
isActive: onTipsPage(m.route.get()),
displayData: null,
};
const councillorsData: SectionGroupAttrs = {
title: 'Councillors',
containsChildren: false,
hasDefaultToggle: showCouncillors
? toggleTreeState['children']['Councillors']['toggledState']
: false,
onclick: (e, toggle: boolean) => {
e.preventDefault();
navigateToSubpage('/council');
setGovernanceToggleTree('children.Councillors.toggledState', toggle);
},
isVisible: showCouncillors,
isUpdated: true,
isActive: onCouncilPage(m.route.get()),
displayData: null,
};
const validatorsData: SectionGroupAttrs = {
title: 'Validators',
containsChildren: false,
hasDefaultToggle: showValidators
? toggleTreeState['children']['Validators']['toggledState']
: false,
onclick: (e, toggle: boolean) => {
e.preventDefault();
navigateToSubpage('/validators');
setGovernanceToggleTree('children.Validators.toggledState', toggle);
},
isVisible: showValidators,
isUpdated: true,
isActive: onValidatorsPage(m.route.get()),
displayData: null,
};
// Delegate
const delegateData: SectionGroupAttrs = {
title: 'Delegate',
containsChildren: false,
hasDefaultToggle: showCompoundOptions
? toggleTreeState['children']['Delegate']['toggledState']
: false,
isVisible: showCompoundOptions,
isUpdated: true,
isActive: m.route.get() === `/${app.activeChainId()}/delegate`,
onclick: (e, toggle: boolean) => {
e.preventDefault();
setGovernanceToggleTree('children.Delegate.toggledState', toggle);
navigateToSubpage('/delegate');
},
displayData: null,
};
const governanceGroupData: SectionGroupAttrs[] = [
membersData,
snapshotData,
delegateData,
treasuryData,
bountyData,
referendaData,
proposalsData,
tipsData,
councillorsData,
validatorsData,
];
const sidebarSectionData: SidebarSectionAttrs = {
title: 'GOVERNANCE',
hasDefaultToggle: toggleTreeState['toggledState'],
onclick: (e, toggle: boolean) => {
e.preventDefault();
setGovernanceToggleTree('toggledState', toggle);
},
displayData: governanceGroupData,
isActive: false,
toggleDisabled: vnode.attrs.mobile,
};
return <SidebarSectionGroup {...sidebarSectionData} />;
}
Example #7
Source File: cosmos_form.tsx From commonwealth with GNU General Public License v3.0 | 4 votes |
view() {
return (
<div class="CreateCommunityForm">
<InputRow
title="RPC URL"
defaultValue={this.state.form.nodeUrl}
placeholder="http://my-rpc.cosmos-chain.com:26657/"
onChangeHandler={async (v) => {
this.state.form.nodeUrl = v;
}}
/>
<InputRow
title="Name"
defaultValue={this.state.form.name}
onChangeHandler={(v) => {
this.state.form.name = v;
this.state.form.id = slugifyPreserveDashes(v);
}}
/>
<IdRow id={this.state.form.id} />
<InputRow
title="Symbol"
defaultValue={this.state.form.symbol}
placeholder="XYZ"
onChangeHandler={(v) => {
this.state.form.symbol = v;
}}
/>
<InputRow
title="Bech32 Prefix"
defaultValue={this.state.form.bech32Prefix}
placeholder="cosmos"
onChangeHandler={async (v) => {
this.state.form.bech32Prefix = v;
}}
/>
<InputRow
title="Decimals"
defaultValue={`${this.state.form.decimals}`}
disabled={true}
onChangeHandler={(v) => {
this.state.form.decimals = +v;
}}
/>
{/* TODO: add alt wallet URL field */}
{...defaultChainRows(this.state.form)}
<CWButton
label="Save changes"
disabled={this.state.saving}
onclick={async () => {
const {
altWalletUrl,
bech32Prefix,
chainString,
ethChainId,
nodeUrl,
} = this.state.form;
this.state.saving = true;
mixpanelBrowserTrack({
event: MixpanelCommunityCreationEvent.CREATE_COMMUNITY_ATTEMPTED,
chainBase: null,
isCustomDomain: app.isCustomDomain(),
communityType: null,
});
try {
const res = await $.post(`${app.serverUrl()}/createChain`, {
alt_wallet_url: altWalletUrl,
base: ChainBase.CosmosSDK,
bech32_prefix: bech32Prefix,
chain_string: chainString,
eth_chain_id: ethChainId,
jwt: app.user.jwt,
network: this.state.form.id,
node_url: nodeUrl,
type: ChainType.Chain,
...this.state.form,
});
await initAppState(false);
m.route.set(`/${res.result.chain?.id}`);
} catch (err) {
this.state.error =
err.responseJSON?.error ||
'Creating new Cosmos community failed';
} finally {
this.state.saving = false;
m.redraw();
}
}}
/>
<ValidationRow error={this.state.error} />
</div>
);
}
Example #8
Source File: erc20_form.tsx From commonwealth with GNU General Public License v3.0 | 4 votes |
view(vnode) {
const validAddress = isAddress(this.state.form.address);
const disableField = !validAddress || !this.state.loaded;
const updateTokenForum = async () => {
if (!this.state.form.address || !this.state.form.ethChainId) return;
this.state.status = '';
this.state.error = '';
this.state.loading = true;
const args = {
address: this.state.form.address,
chain_id: this.state.form.ethChainId,
chain_network: ChainNetwork.ERC20,
url: this.state.form.nodeUrl,
allowUncached: true,
};
try {
console.log('Querying backend for token data');
const res = await $.get(`${app.serverUrl()}/getTokenForum`, args);
if (res.status === 'Success') {
if (res?.token?.name) {
this.state.form.name = res.token.name || '';
this.state.form.id = res.token.id && slugify(res.token.id);
this.state.form.symbol = res.token.symbol || '';
this.state.form.decimals = +res.token.decimals || 18;
this.state.form.iconUrl = res.token.icon_url || '';
if (this.state.form.iconUrl.startsWith('/')) {
this.state.form.iconUrl = `https://commonwealth.im${this.state.form.iconUrl}`;
}
this.state.form.description = res.token.description || '';
this.state.form.website = res.token.website || '';
this.state.form.discord = res.token.discord || '';
this.state.form.element = res.token.element || '';
this.state.form.telegram = res.token.telegram || '';
this.state.form.github = res.token.github || '';
this.state.status = 'Success!';
} else {
// attempt to query ERC20Detailed token info from chain
console.log('Querying chain for ERC info');
const provider = new Web3.providers.WebsocketProvider(args.url);
try {
const ethersProvider = new providers.Web3Provider(provider);
const contract = IERC20Metadata__factory.connect(
args.address,
ethersProvider
);
const name = await contract.name();
const symbol = await contract.symbol();
const decimals = await contract.decimals();
this.state.form.name = name || '';
this.state.form.id = name && slugify(name);
this.state.form.symbol = symbol || '';
this.state.form.decimals = decimals || 18;
this.state.status = 'Success!';
} catch (e) {
this.state.form.name = '';
this.state.form.id = '';
this.state.form.symbol = '';
this.state.form.decimals = 18;
this.state.status = 'Verified token but could not load metadata.';
}
this.state.form.iconUrl = '';
this.state.form.description = '';
this.state.form.website = '';
this.state.form.discord = '';
this.state.form.element = '';
this.state.form.telegram = '';
this.state.form.github = '';
provider.disconnect(1000, 'finished');
}
this.state.loaded = true;
} else {
this.state.error = res.message || 'Failed to load Token Information';
}
} catch (err) {
this.state.error =
err.responseJSON?.error || 'Failed to load Token Information';
}
this.state.loading = false;
m.redraw();
};
return (
<div class="CreateCommunityForm">
{...ethChainRows(vnode.attrs, this.state.form)}
<CWButton
label="Populate fields"
disabled={
this.state.saving ||
!validAddress ||
!this.state.form.ethChainId ||
this.state.loading
}
onclick={async () => {
await updateTokenForum();
}}
/>
<ValidationRow error={this.state.error} status={this.state.status} />
<InputRow
title="Name"
defaultValue={this.state.form.name}
disabled={disableField}
onChangeHandler={(v) => {
this.state.form.name = v;
this.state.form.id = slugifyPreserveDashes(v);
}}
/>
<IdRow id={this.state.form.id} />
<InputRow
title="Symbol"
disabled={disableField}
defaultValue={this.state.form.symbol}
placeholder="XYZ"
onChangeHandler={(v) => {
this.state.form.symbol = v;
}}
/>
{...defaultChainRows(this.state.form, disableField)}
<CWButton
label="Save changes"
disabled={this.state.saving || !validAddress || !this.state.loaded}
onclick={async () => {
const { altWalletUrl, chainString, ethChainId, nodeUrl } =
this.state.form;
this.state.saving = true;
mixpanelBrowserTrack({
event: MixpanelCommunityCreationEvent.CREATE_COMMUNITY_ATTEMPTED,
chainBase: null,
isCustomDomain: app.isCustomDomain(),
communityType: null,
});
try {
const res = await $.post(`${app.serverUrl()}/createChain`, {
alt_wallet_url: altWalletUrl,
base: ChainBase.Ethereum,
chain_string: chainString,
eth_chain_id: ethChainId,
jwt: app.user.jwt,
network: ChainNetwork.ERC20,
node_url: nodeUrl,
type: ChainType.Token,
...this.state.form,
});
await initAppState(false);
m.route.set(`/${res.result.chain?.id}`);
} catch (err) {
notifyError(
err.responseJSON?.error || 'Creating new ERC20 community failed'
);
} finally {
this.state.saving = false;
}
}}
/>
</div>
);
}
Example #9
Source File: erc721_form.tsx From commonwealth with GNU General Public License v3.0 | 4 votes |
view(vnode) {
const validAddress = isAddress(this.state.form.address);
const disableField = !validAddress || !this.state.loaded;
const updateTokenForum = async () => {
if (!this.state.form.address || !this.state.form.ethChainId) return;
this.state.status = '';
this.state.error = '';
this.state.loading = true;
const args = {
address: this.state.form.address,
chain_id: this.state.form.ethChainId,
chain_network: ChainNetwork.ERC721,
url: this.state.form.nodeUrl,
allowUncached: true,
};
try {
console.log('Querying backend for token data');
const res = await $.get(`${app.serverUrl()}/getTokenForum`, args);
if (res.status === 'Success') {
if (res?.token?.name) {
this.state.form.name = res.token.name || '';
this.state.form.id = res.token.id && slugify(res.token.id);
this.state.form.symbol = res.token.symbol || '';
this.state.form.iconUrl = res.token.icon_url || '';
if (this.state.form.iconUrl.startsWith('/')) {
this.state.form.iconUrl = `https://commonwealth.im${this.state.form.iconUrl}`;
}
this.state.form.description = res.token.description || '';
this.state.form.website = res.token.website || '';
this.state.form.discord = res.token.discord || '';
this.state.form.element = res.token.element || '';
this.state.form.telegram = res.token.telegram || '';
this.state.form.github = res.token.github || '';
this.state.status = 'Success!';
} else {
// attempt to query ERC721Detailed token info from chain
console.log('Querying chain for ERC info');
const provider = new Web3.providers.WebsocketProvider(args.url);
try {
const ethersProvider = new providers.Web3Provider(provider);
const contract = IERC721Metadata__factory.connect(
args.address,
ethersProvider
);
const name = await contract.name();
const symbol = await contract.symbol();
const decimals = await contract.decimals();
this.state.form.name = name || '';
this.state.form.id = name && slugify(name);
this.state.form.symbol = symbol || '';
this.state.status = 'Success!';
} catch (e) {
this.state.form.name = '';
this.state.form.id = '';
this.state.form.symbol = '';
this.state.status = 'Verified token but could not load metadata.';
}
this.state.form.iconUrl = '';
this.state.form.description = '';
this.state.form.website = '';
this.state.form.discord = '';
this.state.form.element = '';
this.state.form.telegram = '';
this.state.form.github = '';
provider.disconnect(1000, 'finished');
}
this.state.loaded = true;
} else {
this.state.error = res.message || 'Failed to load Token Information';
}
} catch (err) {
this.state.error =
err.responseJSON?.error || 'Failed to load Token Information';
}
this.state.loading = false;
m.redraw();
};
return (
<div class="CreateCommunityForm">
{...ethChainRows(vnode.attrs, this.state.form)}
<CWButton
label="Populate fields"
disabled={
this.state.saving ||
!validAddress ||
!this.state.form.ethChainId ||
this.state.loading
}
onclick={async () => {
await updateTokenForum();
}}
/>
<ValidationRow error={this.state.error} status={this.state.status} />
<InputRow
title="Name"
defaultValue={this.state.form.name}
disabled={disableField}
onChangeHandler={(v) => {
this.state.form.name = v;
this.state.form.id = slugifyPreserveDashes(v);
}}
/>
<IdRow id={this.state.form.id} />
<InputRow
title="Symbol"
disabled={disableField}
defaultValue={this.state.form.symbol}
placeholder="XYZ"
onChangeHandler={(v) => {
this.state.form.symbol = v;
}}
/>
{...defaultChainRows(this.state.form, disableField)}
<CWButton
label="Save changes"
disabled={this.state.saving || !validAddress || !this.state.loaded}
onclick={async () => {
const { altWalletUrl, chainString, ethChainId, nodeUrl } =
this.state.form;
this.state.saving = true;
mixpanelBrowserTrack({
event: MixpanelCommunityCreationEvent.CREATE_COMMUNITY_ATTEMPTED,
chainBase: null,
isCustomDomain: app.isCustomDomain(),
communityType: null,
});
try {
const res = await $.post(`${app.serverUrl()}/createChain`, {
alt_wallet_url: altWalletUrl,
base: ChainBase.Ethereum,
chain_string: chainString,
eth_chain_id: ethChainId,
jwt: app.user.jwt,
network: ChainNetwork.ERC721,
node_url: nodeUrl,
type: ChainType.Token,
...this.state.form,
});
await initAppState(false);
m.route.set(`/${res.result.chain?.id}`);
} catch (err) {
notifyError(
err.responseJSON?.error ||
'Creating new ERC721 community failed'
);
} finally {
this.state.saving = false;
}
}}
/>
</div>
);
}
Example #10
Source File: eth_dao_form.tsx From commonwealth with GNU General Public License v3.0 | 4 votes |
view(vnode) {
const validAddress = isAddress(this.state.form.address);
const disableField = !validAddress || !this.state.loaded;
const updateDAO = async () => {
if (
!this.state.form.address ||
!this.state.form.ethChainId ||
!this.state.form.nodeUrl
)
return;
this.state.loading = true;
this.state.status = '';
this.state.error = '';
try {
if (this.state.form.network === ChainNetwork.Compound) {
const provider = new Web3.providers.WebsocketProvider(
this.state.form.nodeUrl
);
const compoundApi = new CompoundAPI(
null,
this.state.form.address,
provider
);
await compoundApi.init(this.state.form.tokenName);
if (!compoundApi.Token) {
throw new Error(
'Could not find governance token. Is "Token Name" field valid?'
);
}
const govType = GovernorType[compoundApi.govType];
const tokenType = GovernorTokenType[compoundApi.tokenType];
this.state.status = `Found ${govType} with token type ${tokenType}`;
} else if (this.state.form.network === ChainNetwork.Aave) {
const provider = new Web3.providers.WebsocketProvider(
this.state.form.nodeUrl
);
const aaveApi = new AaveApi(
IAaveGovernanceV2__factory.connect,
this.state.form.address,
provider
);
await aaveApi.init();
this.state.status = `Found Aave type DAO`;
} else {
throw new Error('invalid chain network');
}
} catch (e) {
this.state.error = e.message;
this.state.loading = false;
m.redraw();
return;
}
this.state.loaded = true;
this.state.loading = false;
m.redraw();
};
return (
<div class="CreateCommunityForm">
{...ethChainRows(vnode.attrs, this.state.form)}
<SelectRow
title="DAO Type"
options={[ChainNetwork.Aave, ChainNetwork.Compound]}
value={this.state.form.network}
onchange={(value) => {
this.state.form.network = value;
this.state.loaded = false;
}}
/>
{this.state.form.network === ChainNetwork.Compound && (
<InputRow
title="Token Name (Case Sensitive)"
defaultValue={this.state.form.tokenName}
onChangeHandler={(v) => {
this.state.form.tokenName = v;
this.state.loaded = false;
}}
/>
)}
<CWButton
label="Test contract"
disabled={
this.state.saving ||
!validAddress ||
!this.state.form.ethChainId ||
this.state.loading
}
onclick={async () => {
await updateDAO();
}}
/>
<ValidationRow error={this.state.error} status={this.state.status} />
<InputRow
title="Name"
defaultValue={this.state.form.name}
disabled={disableField}
onChangeHandler={(v) => {
this.state.form.name = v;
this.state.form.id = slugifyPreserveDashes(v);
}}
/>
<IdRow id={this.state.form.id} />
<InputRow
title="Symbol"
disabled={disableField}
defaultValue={this.state.form.symbol}
placeholder="XYZ"
onChangeHandler={(v) => {
this.state.form.symbol = v;
}}
/>
{...defaultChainRows(this.state.form, disableField)}
<CWButton
label="Save changes"
disabled={this.state.saving || !validAddress || !this.state.loaded}
onclick={async () => {
const { chainString, ethChainId, nodeUrl, tokenName } =
this.state.form;
this.state.saving = true;
mixpanelBrowserTrack({
event: MixpanelCommunityCreationEvent.CREATE_COMMUNITY_ATTEMPTED,
chainBase: null,
isCustomDomain: app.isCustomDomain(),
communityType: null,
});
try {
const res = await $.post(`${app.serverUrl()}/createChain`, {
base: ChainBase.Ethereum,
chain_string: chainString,
eth_chain_id: ethChainId,
jwt: app.user.jwt,
node_url: nodeUrl,
token_name: tokenName,
type: ChainType.DAO,
...this.state.form,
});
await initAppState(false);
// TODO: notify about needing to run event migration
m.route.set(`/${res.result.chain?.id}`);
} catch (err) {
notifyError(
err.responseJSON?.error ||
'Creating new ETH DAO community failed'
);
} finally {
this.state.saving = false;
}
}}
/>
</div>
);
}
Example #11
Source File: spl_token_form.tsx From commonwealth with GNU General Public License v3.0 | 4 votes |
view() {
const disableField = !this.state.loaded;
const updateTokenForum = async () => {
this.state.status = '';
this.state.error = '';
let mintPubKey: solw3.PublicKey;
try {
mintPubKey = new solw3.PublicKey(this.state.form.mint);
} catch (e) {
this.state.error = 'Invalid mint address';
return false;
}
if (!mintPubKey) return;
this.state.loading = true;
try {
const url = solw3.clusterApiUrl(this.state.form.cluster);
const connection = new solw3.Connection(url);
const supply = await connection.getTokenSupply(mintPubKey);
const { decimals, amount } = supply.value;
this.state.form.decimals = decimals;
this.state.loaded = true;
this.state.status = `Found ${amount} supply!`;
} catch (err) {
this.state.error = `Error: ${err.message}` || 'Failed to load token';
}
this.state.loading = false;
m.redraw();
};
return (
<div class="CreateCommunityForm">
<SelectRow
title="Cluster"
options={['mainnet-beta', 'testnet', 'devnet']}
value={this.state.form.cluster}
onchange={(value) => {
this.state.form.cluster = value;
this.state.loaded = false;
}}
/>
<InputRow
title="Mint Address"
defaultValue={this.state.form.mint}
placeholder="2sgDUTgTP6e9CrJtexGdba7qZZajVVHf9TiaCtS9Hp3P"
onChangeHandler={(v) => {
this.state.form.mint = v.trim();
this.state.loaded = false;
}}
/>
<CWButton
label="Check address"
disabled={this.state.saving || this.state.loading}
onclick={async () => {
await updateTokenForum();
}}
/>
<ValidationRow error={this.state.error} status={this.state.status} />
<InputRow
title="Name"
defaultValue={this.state.form.name}
disabled={disableField}
onChangeHandler={(v) => {
this.state.form.name = v;
this.state.form.id = slugifyPreserveDashes(v);
}}
/>
<IdRow id={this.state.form.id} />
<InputRow
title="Symbol"
disabled={disableField}
defaultValue={this.state.form.symbol}
placeholder="XYZ"
onChangeHandler={(v) => {
this.state.form.symbol = v;
}}
/>
<InputRow
title="Decimals"
defaultValue={`${this.state.form.decimals}`}
disabled={true}
onChangeHandler={(v) => {
this.state.form.decimals = +v;
}}
/>
{...defaultChainRows(this.state.form, disableField)}
<CWButton
label="Save changes"
disabled={this.state.saving || !this.state.loaded}
onclick={async () => {
const { cluster, iconUrl, mint } = this.state.form;
this.state.saving = true;
mixpanelBrowserTrack({
event: MixpanelCommunityCreationEvent.CREATE_COMMUNITY_ATTEMPTED,
chainBase: null,
isCustomDomain: app.isCustomDomain(),
communityType: null,
});
try {
const res = await $.post(`${app.serverUrl()}/createChain`, {
address: mint,
base: ChainBase.Solana,
icon_url: iconUrl,
jwt: app.user.jwt,
network: ChainNetwork.SPL,
node_url: cluster,
type: ChainType.Token,
...this.state.form,
});
await initAppState(false);
m.route.set(`/${res.result.chain?.id}`);
} catch (err) {
notifyError(
err.responseJSON?.error || 'Creating new ERC20 community failed'
);
} finally {
this.state.saving = false;
}
}}
/>
</div>
);
}
Example #12
Source File: sputnik_form.tsx From commonwealth with GNU General Public License v3.0 | 4 votes |
view(vnode) {
return (
<div class="CreateCommunityForm">
<InputRow
title="DAO Name"
defaultValue={this.state.form.name}
onChangeHandler={(v) => {
this.state.form.name = v.toLowerCase();
}}
placeholder="genesis"
/>
<ToggleRow
title="Network"
defaultValue={this.state.form.isMainnet}
onToggle={(checked) => {
vnode.state.isMainnet = checked;
mixpanelBrowserTrack({
event: MixpanelCommunityCreationEvent.CHAIN_SELECTED,
chainBase: ChainBase.CosmosSDK,
isCustomDomain: app.isCustomDomain(),
communityType: CommunityType.SputnikDao,
});
}}
label={(checked) => {
if (checked !== this.state.form.isMainnet) {
return 'Unknown network!';
}
return checked ? 'Mainnet' : 'Testnet';
}}
/>
{/* TODO: add divider to distinguish on-chain data */}
{...defaultChainRows(this.state.form)}
<CWButton
label="Save changes"
disabled={this.state.saving}
onclick={async () => {
const { iconUrl, name } = this.state.form;
this.state.saving = true;
const isMainnet = this.state.form.isMainnet;
const id = isMainnet
? `${name}.sputnik-dao.near`
: `${name}.sputnikv2.testnet`;
const url = isMainnet
? 'https://rpc.mainnet.near.org'
: 'https://rpc.testnet.near.org';
const createChainArgs = {
base: ChainBase.NEAR,
icon_url: iconUrl,
id,
jwt: app.user.jwt,
name: id,
network: ChainNetwork.Sputnik,
node_url: url,
symbol: isMainnet ? 'NEAR' : 'tNEAR',
type: ChainType.DAO,
...this.state.form,
};
mixpanelBrowserTrack({
event: MixpanelCommunityCreationEvent.CREATE_COMMUNITY_ATTEMPTED,
chainBase: null,
isCustomDomain: app.isCustomDomain(),
communityType: null,
});
try {
// Gabe 2/14/22 Commenting this bit out because it isn't actually used, but maybe it will be someday?
//
// verify the DAO exists
// const config: ConnectConfig = {
// networkId: isMainnet ? 'mainnet' : 'testnet',
// nodeUrl: url,
// keyStore: new keyStores.BrowserLocalStorageKeyStore(
// localStorage
// ),
// };
// const api = await nearConnect(config);
// const rawResult = await api.connection.provider.query<CodeResult>(
// {
// request_type: 'call_function',
// account_id: id,
// method_name: 'get_last_proposal_id',
// args_base64: Buffer.from(JSON.stringify({})).toString(
// 'base64'
// ),
// finality: 'optimistic',
// }
// );
// const _validResponse = JSON.parse(
// Buffer.from(rawResult.result).toString()
// );
// POST object
const res = await $.post(
`${app.serverUrl()}/createChain`,
createChainArgs
);
await initAppState(false);
m.route.set(`${window.location.origin}/${res.result.chain.id}`);
} catch (err) {
notifyError(err.responseJSON?.error || 'Adding DAO failed.');
console.error(err.responseJSON?.error || err.message);
this.state.saving = false;
}
}}
/>
</div>
);
}
Example #13
Source File: starter_community_form.tsx From commonwealth with GNU General Public License v3.0 | 4 votes |
view() {
return (
<div class="CreateCommunityForm">
<InputRow
title="Name"
placeholder="Enter the name of your community"
defaultValue={this.state.form.name}
onChangeHandler={(v) => {
this.state.form.name = v;
this.state.form.id = slugifyPreserveDashes(v);
}}
/>
<IdRow id={this.state.form.id} />
<InputRow
title="Symbol"
defaultValue={this.state.form.symbol}
onChangeHandler={(v) => {
this.state.form.symbol = v;
}}
/>
<SelectRow
title="Base Chain"
options={['cosmos', 'ethereum', 'near']}
value={this.state.form.base}
onchange={(value) => {
this.state.form.base = value;
mixpanelBrowserTrack({
event: MixpanelCommunityCreationEvent.CHAIN_SELECTED,
chainBase: value,
isCustomDomain: app.isCustomDomain(),
communityType: CommunityType.StarterCommunity,
});
}}
/>
{...defaultChainRows(this.state.form)}
<CWButton
label="Save changes"
disabled={this.state.saving || this.state.form.id.length < 1}
onclick={async () => {
this.state.saving = true;
const additionalArgs: {
eth_chain_id?: number;
node_url?: string;
bech32_prefix?: string;
alt_wallet_url?: string;
} = {};
mixpanelBrowserTrack({
event: MixpanelCommunityCreationEvent.CREATE_COMMUNITY_ATTEMPTED,
chainBase: this.state.form.base,
isCustomDomain: app.isCustomDomain(),
communityType: CommunityType.StarterCommunity,
});
// defaults to be overridden when chain is no longer "starter" type
switch (this.state.form.base) {
case ChainBase.CosmosSDK: {
additionalArgs.node_url = 'https://rpc-osmosis.blockapsis.com';
additionalArgs.bech32_prefix = 'osmo';
additionalArgs.alt_wallet_url =
'https://lcd-osmosis.blockapsis.com';
break;
}
case ChainBase.NEAR: {
additionalArgs.node_url = 'https://rpc.mainnet.near.org';
break;
}
case ChainBase.Solana: {
additionalArgs.node_url = 'https://api.mainnet-beta.solana.com';
break;
}
case ChainBase.Substrate: {
additionalArgs.node_url = 'wss://mainnet.edgewa.re';
break;
}
case ChainBase.Ethereum:
default: {
additionalArgs.eth_chain_id = 1;
additionalArgs.node_url =
'wss://eth-mainnet.alchemyapi.io/v2/BCNLWCaGqaXwCDHlZymPy3HpjXSxK7j_';
additionalArgs.alt_wallet_url =
'https://eth-mainnet.alchemyapi.io/v2/BCNLWCaGqaXwCDHlZymPy3HpjXSxK7j_';
break;
}
}
const {
id,
name,
symbol,
iconUrl,
description,
website,
discord,
telegram,
github,
element,
base,
} = this.state.form;
try {
const res = await $.post(`${app.serverUrl()}/createChain`, {
jwt: app.user.jwt,
address: '',
type: ChainType.Offchain,
network: baseToNetwork(this.state.form.base),
icon_url: iconUrl,
id,
name,
symbol,
base,
description,
discord,
element,
github,
telegram,
website,
...additionalArgs,
});
await initAppState(false);
m.route.set(`/${res.result.chain?.id}`);
} catch (err) {
notifyError(
err.responseJSON?.error ||
'Creating new starter community failed'
);
} finally {
this.state.saving = false;
}
}}
/>
</div>
);
}
Example #14
Source File: substrate_form.tsx From commonwealth with GNU General Public License v3.0 | 4 votes |
view() {
return (
<div class="CreateCommunityForm">
<InputRow
title="Name"
defaultValue={this.state.form.name}
onChangeHandler={(v) => {
this.state.form.name = v;
}}
/>
<InputRow
title="Node URL"
defaultValue={this.state.form.nodeUrl}
placeholder="wss://"
onChangeHandler={(v) => {
this.state.form.nodeUrl = v;
}}
/>
<InputRow
title="Symbol"
defaultValue={this.state.form.symbol}
placeholder="XYZ"
onChangeHandler={(v) => {
this.state.form.symbol = v;
}}
/>
<InputRow
title="Spec (JSON)"
defaultValue={this.state.form.substrateSpec}
// TODO: how to make this resizable vertically?
// looks like CUI specifies an !important height tag, which prevents this
textarea={true}
placeholder='{"types": {"Address": "MultiAddress", "ChainId": "u8", "Reveals": "Vec<(AccountId, Vec<VoteOutcome>)>", "Balance2": "u128", "VoteData": {"stage": "VoteStage", "initiator": "AccountId", "vote_type": "VoteType", "tally_type": "TallyType", "is_commit_reveal": "bool"}, "VoteType": {"_enum": ["Binary", "MultiOption", "RankedChoice"]}, "TallyType": {"_enum": ["OnePerson", "OneCoin"]}, "VoteStage": {"_enum": ["PreVoting", "Commit", "Voting", "Completed"]}, "ResourceId": "[u8; 32]", "VoteRecord": {"id": "u64", "data": "VoteData", "reveals": "Reveals", "outcomes": "Vec<VoteOutcome>", "commitments": "Commitments"}, "AccountInfo": "AccountInfoWithRefCount", "Commitments": "Vec<(AccountId, VoteOutcome)>", "VoteOutcome": "[u8; 32]", "VotingTally": "Option<Vec<(VoteOutcome, u128)>>", "DepositNonce": "u64", "LookupSource": "MultiAddress", "ProposalTitle": "Bytes", "ProposalVotes": {"staus": "ProposalStatus", "expiry": "BlockNumber", "votes_for": "Vec<AccountId>", "votes_against": "Vec<AccountId>"}, "ProposalRecord": {"index": "u32", "stage": "VoteStage", "title": "Text", "author": "AccountId", "vote_id": "u64", "contents": "Text", "transition_time": "u32"}, "ProposalStatus": {"_enum": ["Initiated", "Approved", "Rejected"]}, "ProposalContents": "Bytes"}}'
onChangeHandler={(v) => {
this.state.form.substrateSpec = v;
}}
/>
<CWButton
label="Test Connection"
className="button-margin-bottom"
onclick={async () => {
// deinit substrate API if one exists
if (app.chain?.apiInitialized) {
await app.chain.deinit();
}
// create new API
const provider = new WsProvider(
constructSubstrateUrl(this.state.form.nodeUrl),
false
);
try {
await provider.connect();
const api = await ApiPromise.create({
throwOnConnect: true,
provider,
...JSON.parse(this.state.form.substrateSpec),
});
await api.disconnect();
notifySuccess('Test has passed');
} catch (err) {
console.error(err.message);
notifyError('Test API initialization failed');
}
}}
/>
{...defaultChainRows(this.state.form)}
<CWButton
label="Save changes"
disabled={this.state.saving}
onclick={async () => {
const { name, nodeUrl, iconUrl, substrateSpec } = this.state.form;
mixpanelBrowserTrack({
event: MixpanelCommunityCreationEvent.CREATE_COMMUNITY_ATTEMPTED,
chainBase: null,
isCustomDomain: app.isCustomDomain(),
communityType: null,
});
try {
JSON.parse(substrateSpec);
} catch (err) {
notifyError('Spec provided has invalid JSON');
return;
}
this.state.saving = true;
$.post(`${app.serverUrl()}/createChain`, {
base: ChainBase.Substrate,
icon_url: iconUrl,
id: slugify(name),
jwt: app.user.jwt,
network: slugify(name),
node_url: nodeUrl,
substrate_spec: substrateSpec,
type: ChainType.Chain,
...this.state.form,
})
.then(async (res) => {
await initAppState(false);
m.route.set(`/${res.result.chain.id}`);
})
.catch((err: any) => {
notifyError(
err.responseJSON?.error || 'Creating new community failed'
);
})
.always(() => {
this.state.saving = false;
});
}}
/>
</div>
);
}