To build a flying car that actually flies:
-
Make sure that the car is drivable at all times.
-
Ensure that the modularity inspires maintainable reusability.
-
Meditate.
Use node version ^18.12
- Install dependencies
pnpm install
- Refresh known Schains
pnpm compile:chains
- Run DApp locally
pnpm dev:app
- Or, Build for production
pnpm build:app
Build output goes to app/dist
, from where it can be deployed to any static host.
This repo follows a lite microfrontend architecture, which means there will be parallels in design decisions, but not a zealous overlap. On further reviews, if enough antipatterns are found, a suitable name for this architecture should be meshfrontend.
The aim is to strike a balance between:
- Re-usability of features by ecosystem projects
- Maintainable contributions from across communities
For re-usability across the ecosystem, following is distinctly made available from the repository.
๐ฆ Intuitive react hook libraries integrated with SKALE Network - @skalenetwork/feat
๐ฎ UI compositions including various flows for Schain management - @skalenetwork/admin-ui/screens
๐งฑ Reusable common components following SKALE's design system, built with headless accessible libraries - @skalenetwork/ux/components
๐ง JSX UI Elements with some context dependency - @skalenetwork/ux/elements
๐ฅฝ Well-typed interfaces exported alongside relevant modules
๐ Package exports can currently only be imported as TS, and require necessary aliasing in
tsconfig
, and in js bundling. Example with vite+TS can be found in/app
SKALE Network capabilities are divided into features by usability domains.
All features require wagmi
+ react-query
context, exporting:
- TypeScript utility functions
- React TS hooks
Current features include:
- network (primitives)
- control
- analytics
- bridge (token bridging)
- icm (interchain messaging)
- multisig
- storage
Following is a breakdown of the foundational feature.
@/features/network
Network feature offers extensive support for network building blocks, such as SKALE pre-deployed contracts.
All transactional operations are integrated with SKALE access control.
- Use typed contracts
- Use utility APIs on top of contracts
- Use convenience hooks
Network feature is based on a configuration layer.
๐ Configuration layer is independently scalable. It employs the least common format, which can be versioned, and extended to match change in the network complexity.
None-to-slow-changing data
address.ts
All unique preset addresseschains/*.ts
Recognized SChains using a standardChain
type, IDed bychainName
.
Slow-to-medium-changing data
Manifests cater to contracts, ABIs, and wrapper APIs.
๐ Manifests can best evolve to become compile-targets of releases made within
skalenetwork/*
. Following that, they may become a standalone distribution.
-
manifest.ts
Re-export of manifests and utility methods -
contract.ts
Entry point of following manifests, indexing pre-deployed and other known network contracts withContractId
-
abi/abi.ts
Re-export of individual ABIsabi/abi-*.ts
, indexed byContractId
-
api.ts
Re-export of standard initiators for individual APIs imported from various ecosystem libraries, indexed byContractId
Why redistribute JSON ABIs as .ts files? We need the narrowest
Abi
type, producible by aconst
assertion. TS currently doesn't (want to) support JSONas const
microsoft/TypeScript#32063
Example usage of exposed getters:
const address = getSContractProp('CONFIG_CONTROLLER', 'address');
const abi = getAbi('CONFIG_CONTROLLER');
// and reverse
const contractId = build.contractIdFromAddress(address);
const { abi, address } = build.addressAbiPair(contractId);
Medium-to-fast changing data
Registry is a loose implementation around the idea of registering off-chain / near-chain metadata within a module.
Presently registered metadata includes:
- chainlists
skale-network
:metadata/mainnet/chains.json
admin-ui
:metadata/roles.json
It may help to think of screens as portals to stateful features.
Screens are composed of UI widgets linked to UI flows that execute operations on SChain.
They are like pages served in any frontend; Except, all screens are exported independent of each other, and usable in any DApp.
pnpm install -P @skalenetwork/feat
All react hooks are operational only within wagmi
context.
/** in your entrypoint file **/
// within main renderer
<WagmiConfig client={wagmiClient}>
<App />
</WagmiConfig>
Refer to wagmi docs for complete setup example.
Read a single value from a pre-deployed contract
const { data } = useSContractRead('TOKEN_MANAGER_ERC20', {
name: 'automaticDeploy',
});
Read multiple values from a pre-deployed contract
// multi-read fits best with TS for similarly typed return values
const { data, status, refetch } = useSContractReads('CONFIG_CONTROLLER', {
reads: [
{
name: 'isMTMEnabled',
},
{
name: 'isFCDEnabled',
},
],
});
Write to a pre-deployed contract
import { useSContractWrite } from '@skalenetwork/admin-ui/features';
const writer = useSContractWrite('TOKEN_MANAGER_LINKER', {
name: 'connectSChain',
args: ['staging-aware-chief-gianfar'],
});
handle with side effects
const { isLoading, isSuccess, isConfirmed, receipt } = writer;
useEffect(() => {
// when confirmed
}, [receipt]);
const handleSubmit = useCallback(() => {
writer?.write();
}. [writer.write]);
handle with promises
const handleSubmitWithConfirm = useCallback(async () => {
if (!writer.writeAsync) return;
const receipt = await writer.writeAsync(true);
}, [writer.writeAsync]);
What else is in the writer
?
writer
exposes status
of the contract mutation through its lifecycle.
- from
idle
- through
loading
- to
success
orerror
- back to
idle
if auto or manuallyreset()
const { eoa, mnm, ...rest } = writer;
-
eoa
writer where Externally Owned Account (EOA) is signer -
mnm
writer where EOA is owner ofMultiSig
, and submits a transaction that is routed throughMarionette
-
rest
is copy of eithereoa
ormnm
, whichever is authorized, where precedence is given toeoa
writer.writeAsync
or writer.write
execute an already prepared transaction. These methods will only be available if:
args
are passed as expected intouseSContractWrite
options- there are no authorization exceptions
- there are no
require
failures in method call
๐ SKALE Contract writer hook performs eager validation on destination method call. This validation works as expected in case of EOA-signed transactions.
โ๏ธ Learn more about underlying contract call preparation done bywagmi
.
โ ๏ธ The eager validation may cause wallet injected scripts to log errors pre-emptively in console. Example:MetaMask - RPC Error
Both writer
s extend the return data of wagmi
.useContractWrite
, with more state values.
const { action, multisigData, receipt, isConfirmed, isFailed } = rest;
const natureOfDestinationContractCallThroughMariontteViaMultisig = {
isConfirmExistingIdenticalTx: action === 'confirm',
isExecuteExistingIdenticalTx: action === 'execute',
isSubmitNewTx: action === 'submit',
};
const countMultisigConfirmations = multisigData?.countConfirmed;
const countMultisigRequiredConfirmations = multisigData?.countRequired;
Guard UI actions from unready writer
<button disabled={!!writer.writeAsync}>Guarded Action</button>
Guard and guide action with a well-formatted button using SButton
element from @skalenetwork/ux/elements
<SButton writer={writer}>Guarded Action</SButton>
Preview a guarded action button without writer
execution on click
// usage as a preview that opens a separate flow
<SButton writer={writer} noWrite onClick={(e) => openModal()}>
Open Modal
</SButton>
@/features/icm
Among features, ICM (inter-chain messaging) is a domain that would horizontally complete @/features
.
Diagram highlighting typical cross-chain interaction. On the contrary, arbitrary message passing is more suitable for icm
whereas multsig
could be one of the consumers of icm
when supporting foreign multisigs.