@stratosphere-network/token
This package provides a Token
class to deploy and manage upgradeable ERC20 tokens on supported blockchains using the UUPS proxy pattern.
Features
- Upgradeable Tokens: Deploy tokens as UUPS proxies, allowing you to upgrade the underlying contract logic later without requiring users to migrate.
- Role-Based Access Control: Manage permissions with distinct roles (
MINTER_ROLE
,BURNER_ROLE
,PAUSER_ROLE
,UPGRADER_ROLE
) instead of a single owner. - Factory-Based Deployment: A central factory contract deploys lightweight proxy contracts, saving significant gas fees for your users.
- Comprehensive SDK: A simple SDK to handle deployment, role management, feature toggling, and upgrades.
Installation
To install the package, you can use npm or yarn:
npm install @stratosphere-network/token
# or
yarn add @stratosphere-network/token
Usage
First, import the Token
class and the necessary types from the package. If you plan to use an existing ethers.Wallet
instance for deployment, you'll also need to import ethers
:
First, import the Token
class and the necessary types from the package. If you plan to use an existing ethers.Wallet
instance, you'll also need to import ethers
:
import Token, { TokenConfig, Chain } from "@stratosphere-network/token";
import { ethers } from "ethers"; // Required if providing your own ethers.Wallet instance
Next, create an instance of the Token
class:
const tokenClient = new Token();
Deploying a Token
To deploy a new token, you'll need to provide a tokenConfig
object. The deployer's credentials for the deployToken
method can be supplied in one of the following ways:
deployerPrivateKey
(string, optional): The private key of the deployer's account.walletInstance
(ethers.Wallet
, optional): An existingethers.Wallet
object from theethers
library.
You must provide at least one of these to the deployToken
method.
- If only
deployerPrivateKey
is provided, a newethers.Wallet
is created internally using a default provider for the specified chain. - If only
walletInstance
is provided, that instance is used directly for deploying. If both
const sdk = new Token();deployerPrivateKey
andwalletInstance
are provided, a newethers.Wallet
is created using thedeployerPrivateKey
and the provider from the suppliedwalletInstance
.
### 1. Deploying an Upgradeable Token
To deploy a new token, provide a `tokenConfig` object. The `deployToken` method returns an object containing the `transactionHash` and the `tokenAddress` of the newly created proxy contract.
The address that deploys the token will automatically be granted the `DEFAULT_ADMIN_ROLE`, as well as the `MINTER_ROLE`, `BURNER_ROLE`, `PAUSER_ROLE`, and `UPGRADER_ROLE`.
Currently, token deployment is **only supported on Berachain Mainnet**.
```typescript
// Ensure tokenClient is instantiated as shown in the "Usage" section:
// import Token from "@stratosphere-network/token";
// const tokenClient = new Token();
async function deployMyToken() {
const tokenConfig: TokenConfig = {
name: "My Awesome Token",
symbol: "MAT",
totalSupply: "1000000000000000000000000", // 1 million tokens with 18 decimals
decimals: 18,
chain: Chain.BERACHAIN_MAINNET, // Ensure this matches supported chains
isMintable: true, // Optional: Can the token supply be increased after deployment? Defaults to false.
isBurnable: true, // Optional: Can tokens be burned to reduce supply? Defaults to false.
isPausable: false, // Optional: Can token transfers be paused? Defaults to false.
};
// --- Option 1: Using deployerPrivateKey (simplest for quick start) ---
const deployerPrivateKey = "YOUR_DEPLOYER_PRIVATE_KEY"; // Replace with actual private key
try {
console.log("Attempting to deploy with private key...");
const txHashPk = await tokenClient.deployToken(
tokenConfig,
deployerPrivateKey
// walletInstance is omitted or undefined here
);
console.log(
"Token deployment with private key successful! Transaction Hash:",
txHashPk
);
} catch (error) {
console.error("Error deploying token with private key:", error);
}
// --- Option 2: Using an existing ethers.Wallet instance ---
// This requires 'ethers' to be imported and a wallet instance prepared.
// Example setup (replace with your actual provider and private key/mnemonic):
/*
// Ensure ethers is imported: import { ethers } from "ethers";
// const BERACHAIN_RPC_URL = "https://mainnet.rpc.berachain.com/"; // Replace with actual official RPC for Berachain
// const provider = new ethers.JsonRpcProvider(BERACHAIN_RPC_URL);
// const existingWallet = new ethers.Wallet(deployerPrivateKey, provider); // Or from a connected wallet like MetaMask
try {
console.log("Attempting to deploy with wallet instance...");
// Note: Pass undefined for deployerPrivateKey if primarily using walletInstance
const txHashWallet = await tokenClient.deployToken(
tokenConfig,
undefined,
existingWallet
);
console.log(
"Token deployment with wallet instance successful! Transaction Hash:",
txHashWallet
);
} catch (error) {
console.error("Error deploying token with wallet instance:", error);
}
*/
}
deployMyToken();
Note: Ensure your deployer account has enough native currency (e.g., BERA on Berachain) to cover the gas fees for the deployment transaction.
TokenConfig Parameters
name
(string): The name of your token (e.g., "My Awesome Token").symbol
(string): The symbol for your token (e.g., "MAT").totalSupply
(string): The total supply of your token, expressed as a string in its smallest unit (e.g., wei for an 18-decimal token).decimals
(number): The number of decimal places your token will have (typically 18).chain
(Chain): The chain to deploy the token on. Currently, onlyChain.BERACHAIN_MAINNET
is supported.isMintable
(boolean, optional): Determines if new tokens can be minted after initial deployment. Defaults tofalse
if not specified.isBurnable
(boolean, optional): Determines if tokens can be burned (destroyed) after deployment. Defaults tofalse
if not specified.
// Ensure sdk is instantiated as shown above.isPausable
(boolean, optional): Determines if token transfers and other operations can be paused. Defaults tofalse
if not specified.
async function deployNewToken() { const tokenConfig: TokenConfig = { name: "My Upgradeable Token", symbol: "MUT", totalSupply: "1000000000000000000000000", // 1 million tokens with 18 decimals decimals: 18, chain: Chain.BERACHAIN_MAINNET, };
// Provide the private key of the deployer's account. // Ensure this account has enough native currency (e.g., BERA) to pay for gas. const deployerPrivateKey = "YOUR_DEPLOYER_PRIVATE_KEY";
try {
console.log("Attempting to deploy token...");
const { transactionHash, tokenAddress } = await sdk.deployToken(
tokenConfig,
deployerPrivateKey
);
console.log("Token deployment successful!");
console.log(- Transaction Hash: ${transactionHash}
);
console.log(- Token Proxy Address: ${tokenAddress}
); // This is the address to interact with
return tokenAddress;
} catch (error) {
console.error("Error deploying token:", error);
}
}
### 2. Managing the Token
Once deployed, you can manage the token using its proxy address.
#### Role Management
You can grant and revoke roles to other addresses.
```typescript
// You need the token's address from the deployment step.
const tokenAddress = "0xYourTokenProxyAddress";
const anotherAddress = "0x...someOtherAddress";
const adminPrivateKey = "YOUR_DEPLOYER_PRIVATE_KEY"; // The key of an address with DEFAULT_ADMIN_ROLE
// Grant the MINTER_ROLE
await sdk.grantRole(
Chain.BERACHAIN_MAINNET,
tokenAddress,
"MINTER_ROLE",
anotherAddress,
adminPrivateKey
);
console.log("MINTER_ROLE granted.");
// Check if an address has a role
const hasRole = await sdk.hasRole(
Chain.BERACHAIN_MAINNET,
tokenAddress,
"MINTER_ROLE",
anotherAddress
);
console.log(`Does the address have MINTER_ROLE? ${hasRole}`); // true
// Revoke the MINTER_ROLE
await sdk.revokeRole(
Chain.BERACHAIN_MAINNET,
tokenAddress,
"MINTER_ROLE",
anotherAddress,
adminPrivateKey
);
console.log("MINTER_ROLE revoked.");
Feature Control
Enable or disable token transfers, and pause or unpause the contract.
const tokenAddress = "0xYourTokenProxyAddress";
const pauserPrivateKey = "YOUR_DEPLOYER_PRIVATE_KEY"; // The key of an address with PAUSER_ROLE
const adminPrivateKey = "YOUR_DEPLOYER_PRIVATE_KEY"; // The key of an address with DEFAULT_ADMIN_ROLE
// Enable token transfers for all users
await sdk.setTransferable(
Chain.BERACHAIN_MAINNET,
tokenAddress,
true,
adminPrivateKey
);
console.log("Token transfers are now enabled for everyone.");
// Pause the contract
await sdk.pause(Chain.BERACHAIN_MAINNET, tokenAddress, pauserPrivateKey);
console.log("Contract is paused.");
// Unpause the contract
await sdk.unpause(Chain.BERACHAIN_MAINNET, tokenAddress, pauserPrivateKey);
console.log("Contract is unpaused.");
3. Upgrading the Token
If you deploy a new version of the Token.sol
implementation contract, you can upgrade your proxies to point to the new logic.
const tokenAddress = "0xYourTokenProxyAddress";
const newImplementationAddress = "0x...addressOfNewTokenImplementation";
const upgraderPrivateKey = "YOUR_DEPLOYER_PRIVATE_KEY"; // The key of an address with UPGRADER_ROLE
await sdk.upgradeTo(
Chain.BERACHAIN_MAINNET,
tokenAddress,
newImplementationAddress,
upgraderPrivateKey
);
console.log("Token proxy has been upgraded to the new implementation!");
API Reference
TokenConfig
Parameters
name
(string): The name of your token (e.g., "My Upgradeable Token").symbol
(string): The symbol for your token (e.g., "MUT").totalSupply
(string): The initial supply of your token, expressed as a string in its smallest unit (e.g., wei for an 18-decimal token). This supply is minted to the deployer.decimals
(number): The number of decimal places your token will have (typically 18).chain
(Chain): The chain to deploy the token on. Currently, onlyChain.BERACHAIN_MAINNET
is supported.
SDK Methods
All methods that perform on-chain transactions require either a deployerPrivateKey
or a walletInstance
to be passed as the final argument.
deployToken(config, privateKey?, wallet?)
: Deploys a new token proxy. Returns{ transactionHash, tokenAddress }
.grantRole(chain, tokenAddress, role, toAddress, ...)
: Grants a role to an address.revokeRole(chain, tokenAddress, role, fromAddress, ...)
: Revokes a role from an address.hasRole(chain, tokenAddress, role, address)
: Checks if an address has a specific role. Returns a boolean.setTransferable(chain, tokenAddress, isTransferable, ...)
: Enables or disables token transfers for non-admins.pause(chain, tokenAddress, ...)
: Pauses all token transfers, mints, and burns.unpause(chain, tokenAddress, ...)
: Resumes a paused contract.upgradeTo(chain, tokenAddress, newImplementation, ...)
: Upgrades the token proxy to a new implementation contract.
role
parameter must be one of: "MINTER_ROLE"
, "BURNER_ROLE"
, "PAUSER_ROLE"
, "UPGRADER_ROLE"
.
5efa2a7fe3f1a6bd0d3e705d1dd6bdc7f677cdb2