Problem: transferring phone numbers between operators is an opaque and unreliable process. We've personally been hit with issues like old operator deactivating phone number in old system, new operator not activating on new system and phone number being reassigned to someone else (!) before problem could be solved. Our proposal decentralizes the phone number transfer process, preventing such issues in a trustless way.
Our system consists of:
- Central Authorithies - accounts representing government actors responsible for initial assignment of phone number pools to operators
- Operators - accounts representing telcos
- Phone Numbers - PSP34 NFTs that represent phone numbers - owned and managed by Operators
- End users - accounts representing people that actually use phone numbers - stored in NFTs using PSP34Metadata extension
Phone transfer process:
- End user initiates it on
transfer_escrowcontract- end user must also PSP34-approve transfers of the phone number by the transfer contract
- Both operators must approve the transfer
- After approvals, any party can trigger the execution of the transfer
- Add
//Aliceaccount to your wallet (instruction). This is the Central Authority account. - Add
Plusaccount to your wallet (backup json to import in Polkadot.js extension; password ispassword). This is an account of one of the operators. - Add
Playaccount to your wallet (backup json to import in Polkadot.js extension; password ispassword). This is an account of one of the operators. - Open app:
- go to Demo app on Vercel
- or:
- Install pnpm (recommended via Node.js Corepack or
npm i -g pnpm) - Run
pnpm run dev
- Install pnpm (recommended via Node.js Corepack or
Sample flow:
- Connect wallet as the Central Authority (Alice)
- Go to
REGISTER NEW NUMBERpage, enter any phone number and choosePlusas the operator. You'll be asked to sign 2 transactions. - Open page in new tab and connect wallet as the
Plusaccount (see import file in Quick start guide) - Go to
PHONE NUMBERSpage, find the number created by CA (you may need to select higher number of rows per page to see it) and click the+icon - This assignes given phone number to specific end user - provide some wallet address that you own - NOT Alice and not Operators.
- After transaction is confirmed, switch the connected wallet account to the end user.
- Click the bell-with-a-plus icon and select
Playas the operator you want to transfer to. This initiates process of transferring your number to new operator. - Switch the connected wallet account to
Plusaccount and go toTRANSFER REQUESTSpage - Find the new transfer request and click the checkmark button - this will approve the request as
Plus - Do steps 8. and 9. again but as
Playaccount - Refresh the page, connect as
Plusand go toTRANSFER REQUESTSagain. You will now see that the status of the transfer request isREADY - Click the action button for the transfer. This will execute the transfer, making
Playthe new operator of this phone number.
Contracts are deployed on Aleph Zero Testnet.
phone_numbers:5CsNRcrtZ1XySxdpTWC45vaRS9U11PDjePqDh52YNB4boubUtransfer_escrow:5E1JJRDqCH3g8ku5nND5umxqEa1onVuEaH9VU6zeWqe885Gw
This is a full-stack dApp boilerplate for ink! smart contracts with an integrated frontend. It can be used to quickly start developing your hackathon idea or to scaffold a production-ready Web3 application.
The project is part of a Scio Labs initiative to improve the developer experience in the ink! ecosystem and a proud member of the Aleph Zero EFP. π
Other projects include:
create-ink-appCLI (Coming soon)ink!athonBoilerplateuseInkathonHooks & Utility Libraryzink!Smart Contract Macros
Join the discussion in our Telegram Group π¬
If you want to contribute, please read our Contributor Guidelines π
Table of Contents:
- DeTelco
- ink!athon Boilerplate
The boilerplate comes with a small sample ink! Greeter contract which stores a message (the "greeting") and allows anyone to update it. The frontend contains simple UI components to connect your wallet and interact with the contract (i.e. read & write the message). Try it out live on inkathon.xyz.
The frontend works out of the box, without a local node running, as the sample contract is pre-deployed on certain live testnets (i.e. alephzero-testnet and shibuya). Necessary deployment metadata and addresses are provided under contracts/deployments/.
Pre-requisites:
- Setup Node.js v18+ (recommended via nvm with
nvm install 18)- Install pnpm (recommended via Node.js Corepack or
npm i -g pnpm)- Clone this repository
Special Instructions for Windows Users
[!IMPORTANT]
Windows users must either use WSL (recommended) or a custom shell like Git Bash. PowerShell is not supported.
Pre-requisites when using WSL for Linux:
- Install WSL and execute all commands in the WSL terminal
- Setup Node.js v18+ (recommended via nvm with
nvm install 18)- Install the following npm packages globally:
npm i -g npmnpm i -g pnpm node-gyp make- Clone this repository into the WSL file system (e.g.
/home/<user>/inkathon).Tip: You can enter
\\wsl$\in the top bar of the Windows Explorer to access the WSL file system visually.
# Install dependencies (once)
# NOTE: This automatically creates an `.env.local` file
pnpm install
# Start Next.js frontend
pnpm run devOptionally, to enable simple-git-hooks (for automatic linting & formatting when committing), you can run the following command once: pnpm simple-git-hooks.
The contracts/package.json file contains shorthand scripts for building, testing, and deploying your contracts. To run these scripts, you need to set contracts/ as the active working directory in your terminal.
Pre-requisites:
- Install Rust via the Substrate Docs (skip the "Compile a Substrate node" section)
- Install
cargo contract- Install
substrate-contracts-node
# Build contracts and move artifacts to `contracts/deployments/{contract}/` folders
pnpm run build
# Start local node with persistence (contracts stay deployed after restart)
# NOTE: When using Brave, shields have to be taken down for the UIs
pnpm run node
## IMPORTANT: Open a separate terminal window and keep the node running
# Deploy the contracts on the local node
pnpm run deployAlternatively, you can also deploy contracts manually using Contracts UI (pnpm contracts-ui) in the browser.
Open the frontend/.env.local file and set the NEXT_PUBLIC_DEFAULT_CHAIN variable to development. Then restart the frontend and you should be able to interact with the contracts deployed on your local node.
Read more about environment variables and all available chain constants in the Environment Variables section below.
There are multiple places where you need to insert your project's name and identifier. Most of these occurrences are highlighted with a /* TODO */ comment in the code. You can easily replace them one by one by installing the todo-tree plugin.
Additionally, there are the following un-highlighted occurrences:
- the name of the
inkathon.code-workspacefile - the
package.json's name & metadata in the root directory as well as in thecontracts/andfrontend/packages - the workspace dependency (
@inkathon/contracts) defined infrontend/package.jsonand imported infrontend/src/deployments/deployments.ts
To replace the default Greeter contract or add a new one, you need to do the following:
- Add a new contract directory under
contracts/src/ - Add it as another workspace member to the
contracts/Cargo.tomlfile - Add another deployment script or adjust
contracts/scripts/deploy.ts - Adjust the
ContractIdsenum andgetDeploymentsfunction infrontend/src/deployments/deployments.ts
Adding custom scripts is useful to interact with your contracts or test certain functionality. Therefore, just duplicate & reuse the contracts/scripts/script.template.ts file and run it via pnpm run script <script-name>. This command will run the TypeScript file directly via tsx.
For general scripts, the same environment variable initialization & configuration applies as described below in the Deployment section (e.g. to change the target network).
The Stack in Detail
- Monorepo Workspace with
contracts/andfrontend/directories as packages. - Package Manager:
pnpmoryarn@stable(Read more in the FAQs section below) - Smart Contract Development: Rust, ink!,
cargo-contract,substrate-contracts-node - Frontend: Next.js (app-dir), React, TypeScript
- Contract Interactions:
polkadot-js,useInkathonReact Hooks & Utility Library (alternatively:useInk) - Styling:
shadcn/ui,tailwindcss - Linting & Formatting:
eslint,prettier,simple-git-hooks,lint-staged
- Contract Interactions:
Styling, linting, and formatting libraries can be fully dropped or replaced with alternatives.
Below you find live examples that use this boilerplate or have a similar setup inspired by it:
- inkathon.xyz β Live demo deployment of this boilerplate
- AZERO.ID β Domain Name Service for Aleph Zero and beyond
- Multiple hackathon projects from ETHWarsaw, HackOnChain, ETHDam, and the Polkadot ink! Hackathon.
Spinning up a deployment via Vercel is pretty straightforward as the necessary settings are already configured in vercel.json. If you haven't cloned the repository yet, you can also use the Deploy button below to create a new repository from this template.
One key element making this boilerplate so flexible is the usage of environment variables to configure the active network in the frontend. This is done by setting the NEXT_PUBLIC_DEFAULT_CHAIN variable in the frontend/.env.local file, or in the Vercel deployment settings respectively.
All Supported Chain Constants
| Network Identifier | Name | Type |
|---|---|---|
development |
οΈLocal Development Node | Testnet |
alephzero-testnet |
Aleph Zero Testnet | Testnet |
rococo |
Rococo | Testnet |
shibuya |
Shibuya Testnet | Testnet |
shiden |
Shiden | Mainnet |
alephzero |
Aleph Zero | Mainnet |
astar |
Astar | Mainnet |
Source: https://github.com/scio-labs/use-inkathon/blob/main/src/chains.ts
[!NOTE]
Chains can also be supplied manually by creating aSubstrateChainobject. If you think a chain is missing, please open an issue or PR.
All environment variables are imported from process.env in frontend/src/config/environment.ts and re-exported from there. For improved type safety, Always only import environment variables from @/config/environment and never directly from process.env.
| Environment Variables | Default Values | Description |
|---|---|---|
NEXT_PUBLIC_DEFAULT_CHAIN *οΈβ£ |
οΈalephzero-testnet |
The network (Substrate-based chain) the frontend should connect to by default and what contract deployment artifacts to import. |
NEXT_PUBLIC_PRODUCTION_MODE |
false |
Optional boolean flag to differentiate production environment (e.g. for SEO or Analytics). |
NEXT_PUBLIC_URL |
http://localhost:3000 |
Optional string that defines the base URL of the frontend (will be auto-inferred from Vercel environment variables). |
NEXT_PUBLIC_SUPPORTED_CHAINS |
β | Optional array with network identifers (e.g. ["alephzero-testnet", "shibuya"]) that are supported by the frontend, if the dApp is supposed to be multi-chain. |
*οΈβ£ Required
In the Getting Started section above, we've already deployed the sample Greeter contract on a local node. To target a live network, we can use the CHAIN environment variable when running the deploy script.
CHAIN=alephzero-testnet pnpm run deployFurther, dynamically loaded environment files with the .env.{chain} naming convention can be used to add additional configuration about the deployer account.
# .env.alephzero-testnet
ACCOUNT_URI=bottom drive obey lake curtain smoke basket hold race lonely fit walk//AliceWhen running the same script again, this deployer account defined there will be used to sign the extrinsic.
Warning
These files are gitignored by default, but you should still be extra cautious when adding sensitive information to them.
It can be helpful to develop in VSCode by opening the workspace file inkathon.code-workspace instead of just the plain directory. This approach offers multiple advantages, like sections in the file explorer, or shortcut actions to open the terminal in the correct directory.
Consider installin the zoma.vscode-auto-open-workspace extension to automatically open the workspace file when opening the directory.
Additionally, the VSCode plugins listed below are recommended as they can be very helpful when working with this boilerplate.
All Recommended Plugins
| Plugin Name | Description |
|---|---|
dbaeumer.vscode-eslint |
Adds ESLint editor support. |
esbenp.prettier-vscode |
Adds Prettier editor support. |
bradlc.vscode-tailwindcss |
Adds tailwindcss editor support. |
rust-lang.rust-analyzer |
Adds Rust language support. |
ink-analyzer.ink-analyzer |
Adds ink! language support. |
tamasfe.even-better-toml |
Adds .toml file support. |
gruntfuggly.todo-tree |
Lists all TODO comments in your workspace. |
wayou.vscode-todo-highlight |
Highlights TODO comments in your workspace. |
mikestead.dotenv |
Adds syntax highlighting for .env files. |
Which package managers are supported? Do I have to use pnpm?
For monorepo workspaces, pnpm is likely the fastest and most reliable choice. When using it though, it's strongly recommended everyone on the team uses it. No installs should be performed nor any other lock files should be committed.
As an alternative, yarn is also supported and can be used for installation. Caveats when using yarn:
- Only the stable version of yarn (currently v3) is supported, not yarn classic (v1).
yarn.lockfiles should be committed instead of.pnpm-lock.yamlfiles.- The
pnpmCLI is still used in manypackage.jsonscripts, so these would have to be adjusted manually.
[!IMPORTANT]
As npm lacks support for theworkspaceimport protocol, it's not compatible with ink!athon.
How to solve `Cannot find module './greeter/development.ts'`?
Sometimes, Next.js doesn't pick up changes (i.e. file creations) in the contracts/deployments/{contract}/ folders correctly. E.g., when you just deployed on a local node for the first time and set the frontend's .env.local to connect to the development network.
To fix this, you can delete the build cache at frontend/.next. This is currently the only solution and will force Next.js to rebuild the project and pick up the new files.
[!NOTE]
To prevent this behavior, thecontracts/package.jsonfile contains a smallpostinstallscript that creates an emptydevelopment.tsfile if none exists.
How to approach styling?
Currently it offers styling via the following options out of the box:
- shadcn/ui - Re-usable components built using Radix UI and Tailwind CSS.
- Vanilla Tailwind CSS styled styles via
classNameand*.module.(s)cssfiles. - Default (S)CSS styles.
[!INFO]
This boilerplate tries to stay as un-opinonated in regards to styling, which means you can use any styling or component library.

