# Wormhole Native Token Transfers on Algorand

This guide provides comprehensive technical documentation for implementing Wormhole’s Native Token Transfers (NTT) framework between Algorand and EVM-compatible chains. The implementation enables bidirectional token transfers across heterogeneous blockchain environments through Wormhole’s verified attestation architecture. This guide has been made considering we have an existing Algorand token. So, a new token is deployed on EVM in `burning` mode. You can deploy in `locking`/`burning` mode as per your requirement. The details of which mode you need is covered in [this](https://dev.algorand.co/resources/bridging#choosing-the-right-deployment-path) section.

### Native Token Transfers (NTT)

Native Token Transfers (NTT) is Wormhole’s framework for transferring tokens across blockchains without requiring liquidity pools. Unlike traditional token bridges that wrap assets or rely on locked liquidity, NTT enables direct token transfers by either burning tokens on the source chain and minting them on the destination, or by locking tokens on one chain while minting representations on others.

The framework gives integrators complete control over how their tokens behave on each chain, including the token standard, metadata, and supply distribution. This level of control makes NTT particularly valuable for projects that want to maintain native token properties across multiple blockchains while ensuring security through Wormhole’s Guardian network.

### Core Components

#### Transceiver

The Transceiver contract serves as the foundational abstraction for all cross-chain communication within the NTT framework. It defines a standardized interface for sending and receiving messages between chains, independent of the underlying messaging protocol. They sit at the boundary between NTT and an underlying General Messaging Protocol (GMP) such as Wormhole, Axelar, LayerZero, or CCIP.

A transceiver does not understand tokens, balances, or rate limits. Its sole responsibility is to move opaque messages across chains and attest to their receipt. This abstraction allows the NTT framework to remain agnostic to the messaging layer. Wormhole is simply one concrete implementation.

#### Wormhole Transceiver

The Wormhole Transceiver represents a concrete implementation of the Transceiver abstraction using the Wormhole messaging protocol. It bridges the generic Transceiver interface with Wormhole’s specific verification model, particularly the Verified Action Approval (VAA) system.

When a Wormhole Transceiver sends a message, it interacts with Wormhole Core to publish a message that will be observed and signed by Wormhole Guardians. This creates a VAA which is a cryptographically verified attestation that a specific action occurred on the source chain. The VAA serves as a decentralized notarization, with signatures from a supermajority of Guardians providing strong security guarantees.

On the receiving side, the Wormhole Transceiver must verify the VAA signatures, check for replay attacks, and validate that the message originates from a known peer contract. This verification process transforms a potentially untrustworthy cross-chain message into a verified fact that can be acted upon with confidence. Each Wormhole Transceiver needs to know its counterpart on other chains. This is where we need to configure peer addresses.

#### Transceiver Manager

The Transceiver Manager serves as the central coordination point for all cross-chain messaging within an NTT deployment. Its primary function is managing the relationship between MessageHandlers (like NttManager) and their configured Transceivers, while tracking attestations for received messages.

This design enables multi-transceiver security. A message handler can require attestations from one or many transceivers before a message is considered valid. This makes it possible to combine multiple GMPs or verification backends for stronger guarantees.

#### Message Handler

The Message Handler abstract contract provides the scaffolding for executing cross-chain messages once they’ve been sufficiently verified. It defines the critical lifecycle methods for message approval and execution, while leaving message-specific logic to concrete implementations.

It encapsulates the threshold logic, checking whether a message has received enough attestations from distinct Transceivers. It then carries out the actual business logic once approval conditions are met. This separation ensures that verification and execution remain distinct phases, preventing premature execution of unverified messages.

#### NttManager

The NTT Manager is the central orchestrator of all token transfers and sits at the heart of the NTT system. When a user wants to transfer tokens cross-chain, they interact exclusively with the NTT Manager, which handles everything from rate limiting to message creation.

The manager’s first responsibility is enforcing rate limits, which protect against potential exploits by limiting how many tokens can move in or out within a given time period. If a transfer exceeds the current capacity, the manager can either reject it or place it in a queue, depending on configuration. This queuing system ensures that even during high-volume periods, transfers can eventually complete once capacity refills.

Beyond rate limiting, the NTT Manager creates unique message identifiers for each transfer by maintaining a sequence counter. This sequence, combined with the source chain ID and contract address, ensures every message can be uniquely identified throughout its lifecycle. The manager also maintains a registry of peer NTT Managers on other chains, knowing exactly where to send messages and from where to accept them.

When receiving messages from other chains, the NTT Manager verifies that sufficient attestations have been collected before executing the transfer. It checks with the Transceiver Manager to confirm that the required number of Transceivers have verified the incoming message. Only after this verification does it call the NTT Token contract to mint or unlock tokens for the recipient.

#### NttToken

The NTT Token contract manages the actual token supply and is the lowest level of the NTT architecture. Its behavior differs significantly between Algorand and EVM chains due to fundamental differences in how these platforms handle tokens.

On Algorand, tokens exist as Algorand Standard Assets (ASAs) that are natively integrated into the protocol layer. The NTT Token contract doesn’t implement the token itself but rather controls the supply of an ASA. In `burning` mode, the ASA is created within the NTT Token contract, meaning the entire supply initially sits in the contract’s account. In locking mode, the ASA already exists externally, and the NTT Token contract receives tokens when they need to be locked.

On EVM chains, the NTT Token contract is an ERC-20 implementation. In `burning` mode, it includes mint and burn functions that the NTT Manager can call. In locking mode, it’s typically an existing ERC-20 contract, and the NTT Manager holds tokens on behalf of users who have transferred them to other chains.

The critical point is that after deployment, both modes function identically from the transfer perspective. Whether tokens are being `burned` or `locked`, the user experience is the same: tokens go into the NTT Token contract when leaving the chain and come out when arriving. The distinction is semantic and affects how circulating supply is calculated, but not the actual mechanics of transfers.

### Message Lifecycle

The lifecycle of a message can be broken down into two stages:

1. Initiating the transfer on the source chain by locking/burning the token.
2. Completing the transfer on the destination chain by unlocking/minting the token.

#### Initiation Phase

The transfer process begins when a user wants to move tokens from one chain to another. On Algorand, the user creates a transaction group that includes both a call to the NTT Manager’s transfer function and an asset transfer that sends the tokens to the NTT Token contract. On EVM chains, the user must first approve the NTT Manager to spend their tokens, then call the transfer function.

The NTT Manager immediately checks its rate limit to determine if the transfer can proceed. If capacity is sufficient, the manager calls the NTT Token contract to lock or burn the tokens. The tokens are removed from circulation on the source chain before any message is sent to the destination chain. In `burning` mode, this reduces the total supply. In locking mode, it moves the tokens into the NTT Token contract’s custody. With the tokens secured, the NTT Manager creates a message containing all transfer details.

The message goes to Wormhole Core, which emits an event that Guardian nodes observe. The Guardians independently validate that the event came from a legitimate source and sign it. Once the threshold of signatures is reached, the VAA becomes available for anyone to query from Wormhole’s network. The transaction succeeds, the user receives a message ID for tracking, and the tokens have been removed from the source chain.

#### Completion Phase

Completion on the destination chain happens in two distinct phases:

The first phase is message reception and attestation. A relayer retrieves the VAA from Wormhole’s network and submits it to the Wormhole Transceiver on the destination chain. The Transceiver first calls Wormhole Core to verify the VAA’s Guardian signatures. The Transceiver also checks that the message came from a known peer Transceiver on the source chain and VAA hasn’t been processed before by checking its hash against a registry of seen messages. This replay protection ensures the same transfer can’t be executed multiple times. The Transceiver Manager records this attestation, associating it with the message digest.

The second phase is message execution. Once threshold has been met, the relayer, the recipient, or any third party, calls the NTT Manager which verifies the message hasn’t been executed already and the message came from a known peer NTT Manager on the source chain.

Rate limiting is checked on the destination chain and if it passes, the NTT Manager calls the NTT Token contract’s mint function, passing the recipient address and amount. In `burning` mode, this creates new tokens. In locking mode, it transfers existing tokens from the contract’s custody. The tokens appear in the recipient’s account, completing the transfer. The transfer is now complete end-to-end.

### Choosing the Right Deployment Path

One of the most important design decisions when adopting NTT is whether you are working with a new token or an existing token. NTT is flexible enough to support both existing tokens and brand new multichain-native tokens. The main tool for this is the choice between **locking** and **burning** mode on each chain.

1. If You Have an **Existing Token on Algorand (ASA)**

- Algorand should use **locking mode**:

- NttToken is configured to hold the existing ASA when users transfer cross-chain.
  - The original ASA continues to be the “canonical” representation of your token.
  - All initial supply is on Algorand, and other chains get supply only when Algorand locks some of its ASA balance and mint or unlock on the other chains.
- All other chains should use **burning mode**:

- On each destination chain, you deploy an NttToken that either wraps a newly created token or directly manages it.
  - The supply on each destination chain increases only when Algorand has locked the corresponding amount.

2. If You Have an **Existing Token on an EVM Chain (ERC-20)**

- The chain with the existing ERC-20 should use **locking mode**:

- NttToken holds locked ERC-20 balances when users bridge out.
  - The original ERC-20 contract remains the canonical representation.
  - All initial supply starts and conceptually lives on that chain.
- All other chains, including Algorand, should use **burning mode**:

- Algorand gets an NttToken that manages an ASA in `burning` mode.
  - Supply on Algorand increases only when tokens have been locked on the origin chain.

3. If You Are Creating a **New Token**

- Every chain uses **burning mode**:

- On Algorand, NttToken creates a new ASA and holds the supply.
  - On each EVM chain, NttToken deploys a new ERC-20 or equivalent managed token.
  - You decide how to distribute the initial supply across chains: mint everything on one chain and move it out via NTT, or mint portions on multiple chains according to your distribution plan.

## Prerequisites

- Node.js [v18.0+](https://nodejs.org/)
- npm [v9.0+](https://nodejs.org/)
- Bun [v1.2.23](https://bun.sh/install)
- Python [3.12+](https://www.python.org/)
- Foundry [latest](https://foundry.paradigm.xyz/)
- AlgoKit [latest](https://dev.algorand.co/algokit/algokit-intro)
- Puya Compiler [v5.3.2](https://dev.algorand.co/concepts/smart-contracts/languages/typescript/#puyats-compiler)

### Required Accounts & Credentials

#### Algorand Testnet

- **Account with funds**: Minimum 10 ALGO for contract deployment and testing
- Save **Account mnemonic**: 25-word recovery phrase. This will be used later while bridging.

#### EVM Chain

- **Wallet with ETH**: Minimum 0.05 ETH for deployment and testing
- Save **Private key** which is 64-character hex string. This will also be used later while bridging.
- **Basescan API key**: From [sepolia.basescan.org/apis](https://sepolia.basescan.org/apis)

### Obtain Testnet Funds

1. ETH Faucets: Get some Base Sepolia Tokens using [Coinbase Faucet](https://www.coinbase.com/faucets/base-ethereum-sepolia-faucet) (`Recommended` \- gives 0.05-0.1 ETH). Other alternatives available are:

- [Alchemy Faucet](https://www.alchemy.com/faucets/base-sepolia)
- [QuickNode Faucet](https://faucet.quicknode.com/base/sepolia)
- [LearnWeb3 Faucet](https://learnweb3.io/faucets/base_sepolia/)

2. Algorand Testnet ALGO: Get some Algorand testnet tokens using [Algorand Testnet Dispenser](https://lora.algokit.io/testnet/fund)

## Algorand Deployment

### Clone contract and install dependencies

```bash
# Clone Folks Finance NTT Contracts repository

git clone https://github.com/Folks-Finance/algorand-ntt-contracts.git

cd algorand-ntt-contracts

# Create Python virtual environment

python3 -m venv venv

source venv/bin/activate  # On Windows: venv\Scripts\activate

# Install Python dependencies

pip install -r requirements.txt

# Install Node.js dependencies

npm install
```

### Build Contracts

Generate TEAL code, ARC56 specs, and TypeScript clients

```bash
npm run build
```

This generates artifacts in the `specs/` folder:

- `*.approval.teal` \- Contract approval programs
- `*.clear.teal` \- Contract clear state programs
- `*.arc56.json` \- Contract interface specifications
- TypeScript client files for contract interaction

### Configure Deployment

Update the `.env` file:

```bash
ALGORAND_TESTNET_ACCOUNT_MNEMONIC="your twenty five word mnemonic phrase here ..."
```

### Deploy Contracts

#### Deploy NttToken Contract

**Option A: For a New ASA (Creating a new token)**

```bash
npm run script deployment/deployNewToken.ts
```

**Option B: For an Existing ASA (Using an existing Algorand token)**

```bash
npm run script deployment/deployExistingToken.ts
```

**Expected Output:**

#### Post-Deployment

For existing token, transfer entire non-circulating supply(apart from tokens you want to send during bridging) to NttToken contract

```bash
algokit goal asset send \
  --from <YOUR_ADDRESS> \
  --to <NTT_TOKEN_APP_ADDRESS> \
  --assetid <ASA_ID> \
  --amount <TOTAL_SUPPLY>
```

For existing token (optional), update the ASA reserve address to be the NttToken contract address

```bash
algokit goal asset config \
  --assetid <ASA_ID> \
  --manager <YOUR_ADDRESS> \
  --reserve <NTT_TOKEN_APP_ADDRESS>
```

#### Deploy NttManager Contract

```bash
npm run script deployment/deployNttManager.ts
```

**Expected Output:**

#### Deploy WormholeTransceiver Contract

```bash
npm run script deployment/deployWormholeTransceiver.ts
```

**Expected Output:**

### Record Deployment Addresses

After deployment, check `deployment/out/contracts.json`. The testnet should be populated with `token`, `nttManager` and `wormholeTransceiver` details.

## EVM Testnet Deployment

### Install NTT CLI

```bash
# Clone NTT repository

git clone --branch 'v1.6.0+cli' --single-branch --depth 1 \
  https://github.com/wormhole-foundation/native-token-transfers.git

cd native-token-transfers

# Install Bun (if not already installed)

curl -fsSL https://bun.sh/install | bash -s "bun-v1.2.23"

# Install dependencies

npm ci

# Install NTT CLI globally

cd cli

./install.sh

# Verify installation

ntt --version
```

### Create NTT Project

```bash
cd ~  # Or your preferred workspace

ntt new my-ntt-deployment

cd my-ntt-deployment

# Initialize for testnet

ntt init Testnet
```

### Deploy ERC20 Token

#### Clone EVM Example NTT Token

```bash
git clone https://github.com/wormhole-foundation/example-ntt-token-evm.git

cd example-ntt-token-evm

forge install
```

#### Configure Token Parameters

Ensure token parameters match Algorand ASA and vice versa.

```bash
ASSET_NAME=<YOUR_ASSET_NAME>

UNIT_NAME=<YOUR_ASSET_UNIT_NAME>

DECIMALS=6  # Must match ASA decimals

DEPLOYER_ADDRESS=<YOUR_EVM_ADDRESS>

OWNER_ADDRESS=<YOUR_EVM_ADDRESS>  # Can be same as deployer
```

#### Override Decimals Function (If Needed)

Alternatively, if your token requires non-standard decimals (not 18), add to `PeerToken.sol`:

```solidity
function decimals() public view virtual override(ERC20) returns (uint8) {
    return 6;  // Match your ASA decimals
}
```

#### Deploy Token Contract

```bash
forge create --rpc-url https://sepolia.base.org \
  --private-key $EVM_TESTNET_PRIVATE_KEY \
  src/PeerToken.sol:PeerToken \
  --broadcast \
  --constructor-args "$ASSET_NAME" "$UNIT_NAME" $DECIMALS $DEPLOYER_ADDRESS $OWNER_ADDRESS
```

**Expected Output:**

Save the deployed token address!

### Deploy NTT Infrastructure

#### Configure Contract Verification (Optional)

For production deployments with verified contracts:

```bash
ntt config set-chain BaseSepolia verifier etherscan
ntt config set-chain BaseSepolia scan_api_key $BASESCAN_API_KEY

# Verify configuration
ntt config get-chain BaseSepolia scan_api_key
```

#### Deploy NTT Contracts

**Without Verification:**

```bash
ntt add-chain BaseSepolia \
  --latest \
  --mode burning \
  --token $YOUR_EVM_TOKEN_ADDRESS \
  --skip-verify
```

**With Verification:**

```bash
ntt add-chain BaseSepolia \
  --latest \
  --mode burning \
  --token $YOUR_EVM_TOKEN_ADDRESS
```

#### Verify Deployment

```bash
ntt status
```

**Expected Output:**

### Record Deployment Addresses

Check `deployment.json` in your NTT project. You should be able to see details of your deployed token.

Save the addresses you get in your `deployment.json` file for peer configuration!

## Cross-Chain Configuration

### Configure Base Sepolia → Algorand Peering

Copy the below addresses

- From Base Sepolia (`deployment.json`): `NttManager` and `WormholeTransceiver`
- From Algorand (`deployment/out/contracts.json`): `nttManager.peerAddress` and `wormholeTransceiver.peerAddress`

#### Set NttManager Peer on Base Sepolia

```bash
export PEER_ADDRESS=<YOUR_PEER_ADDRESS>

export TOKEN_DECIMALS=6 #Token decimals (MUST match ASA)

export NTTMANAGER=<YOUR_NTT_MANAGER>

cast send $NTTMANAGER \
  "setPeer(uint16,bytes32,uint8,uint256)" \
  8 \ # Algorand's Wormhole chain ID
  $PEER_ADDRESS \
  $TOKEN_DECIMALS \
  1000000000000 \
  --rpc-url https://sepolia.base.org \
  --private-key $EVM_TESTNET_PRIVATE_KEY
```

**Expected Output:**

#### Set WormholeTransceiver Peer on Base Sepolia

```bash
cast send $WORMHOLE_TRANSCEIVER \
  "setWormholePeer(uint16,bytes32)" \
  8 \
  $WORMHOLE_TRANSCEIVER_PEER_ADDRESS \
  --rpc-url https://sepolia.base.org \
  --private-key $EVM_TESTNET_PRIVATE_KEY
```

Once you set you can verify it using the below command:

```bash
cast call $WORMHOLE_TRANSCEIVER \
  "getWormholePeer(uint16)(bytes32)" \
  8 \
  --rpc-url https://sepolia.base.org
```

**Expected Output:**

### Configure Algorand → Base Sepolia Peering

Edit `peers` in `algorand-ntt-contracts/deployment/configure.ts`:

```javascript
const peers: NttPeerChain[] = [\
\
  {\
\
    wormholeChainId: 10004,  // Base Sepolia's Wormhole chain ID\
\
    nttManager: <PADDED_NTT_MANAGER_ADDRESS>,\
\
    wormholeTransceiver: <PADDED_WORMHOLE_TRANSCEIVER_ADDRESS>,\
\
  },\
\
];
```

#### Apply Configuration

```bash
cd algorand-ntt-contracts

npm run script deployment/configure.ts
```

**Expected Output:**

## SDK Integration & Testing

### Build NTT SDK

```bash
# Clone the SDK repository

git clone https://github.com/Folks-Finance/wormhole-ntt-sdk.git

cd wormhole-ntt-sdk

# Install dependencies

npm install

# Build the SDK

npm run build
```

### Configure Custom Token

Go to `examples/constants.ts` file. Update your token details and contract addresses in `CUSTOM_NTT_TOKEN_TESTNET_ID`, `CUSTOM_NTT_TOKEN_TESTNET_AVM` and `CUSTOM_NTT_TOKEN_TESTNET_EVM`.

### Use Transfer Script

#### Transfer from AVM to EVM

For transferring from avm to evm use `examples/transfer-avm-to-evm-custom-token.ts`. Update `destChain` according to the EVM testnet you are using. Since we are using base sepolia, we will update:

```javascript
const destChain = TESTNET_FOLKS_CHAIN_ID.BASE_SEPOLIA;
```

Update `YOUR_EVM_ADDRESS` to the address you want to send the tokens as well as the amount(default is 1 TOKEN) to in `convertToGenericAddress`. This transfer function will transfer the TOKENS from Algorand to your configured EVM chain.

```javascript
  const prepareCall = await FolksBridge.prepare.transfer(
    CUSTOM_NTT_TOKEN_TESTNET_ID,
    1_000_000n,
    destChain,
    convertToGenericAddress(<YOUR_EVM_ADDRESS>, ChainType.EVM),
    capabilities,
    quote,
    feePaymentToken
  );
```

In case you don’t have FOLKS token to pay fees, you can update `feePaymentToken` to use native ALGO for fee payment

```javascript
const feePaymentToken = {
  tokenType: TokenType.GAS,
  tokenSymbol: 'ALGO',
  tokenDecimals: 6,
};
```

#### Transfer from EVM to AVM

To transfer from evm to avm use `examples/transfer-evm-to-avm-custom-token.ts`.
Update `chain`, `sourceChain` and `folksChainId` according to the EVM testnet you are using. Since we are using base sepolia, we will update:

```javascript
const chain = baseSepolia;

const folksChainId = TESTNET_FOLKS_CHAIN_ID.BASE_SEPOLIA;

const sourceChain = TESTNET_FOLKS_CHAIN_ID.BASE_SEPOLIA;
```

Update `YOUR_AVM_ADDRESS` to the address you want to send the tokens as well as the amount(default is 1 TOKEN) to in `convertToGenericAddress`. This transfer function will transfer the TOKENS from configured EVM chain to Algorand.

```javascript
  const prepareCall = await FolksBridge.prepare.transfer(
    CUSTOM_NTT_TOKEN_TESTNET_ID,
    1_000_000n,
    destChain,
    convertToGenericAddress(<YOUR_AVM_ADDRESS>, ChainType.AVM),
    capabilities,
    quote,
    feePaymentToken,
  );
```

In case you don’t have FOLKS token to pay fees, you can update `feePaymentToken` to use native ETH token for fee payment

```javascript
const feePaymentToken = {
  tokenType: TokenType.GAS,
  tokenSymbol: 'ETH',
  tokenDecimals: 18,
};
```

### Environment Variable Template

copy the `.env.example` to `.env` file and update the private key for EVM Testnet and add Algorand Testnet Mnemonic:

```bash
EVM_TESTNET_PRIVATE_KEY=<YOUR_EVM_PRIVATE_KEY>

ALGORAND_TESTNET_ACCOUNT_MNEMONIC=<YOUR_ALGORAND_TESTNET_ACCOUNT_MNEMONIC>
```

### Pre-Transfer Checklist

Before running AVM to EVM transfer, ensure:

1. Your Algorand Account has at least 10 ALGO
2. You have enough custom ALGO token you want to send(excluding MBR)
3. Set minter role to NTT Manager Address `cast send $YOUR_EVM_TOKEN_ADDRESS "setMinter(address)" $NTT_MANAGER_ADDRESS --rpc-url https://sepolia.base.org --private-key $EVM_TESTNET_PRIVATE_KEY` to mint tokens for your `EVM_TESTNET_ADDRESS` on destination chain.

Before running EVM to AVM transfer, ensure:

1. At least 0.002 ETH (0.0015 for bridge fee + 0.0005 for gas)
2. You have enough custom ETH token you want to send
3. Must approve Folks Bridge contracts. If using other EVM chains, get the `NTT_EXECUTOR_ADDRESS` from `nttWithExecutor.ts` file: `cast send $YOUR_EVM_TOKEN_ADDRESS  "approve(address,uint256)"  $BASE_SEPOLIA_NTT_EXECUTOR_ADDRESS  $AMOUNT  --rpc-url https://sepolia.base.org  --private-key $EVM_TESTNET_PRIVATE_KEY`
4. Must approve NTTManager. Get `$MANAGER` address of EVM testnet from `deployment.json` file: `cast send $YOUR_EVM_TOKEN_ADDRESS  "approve(address,uint256)"  $MANAGER $AMOUNT  --rpc-url https://sepolia.base.org  --private-key $EVM_TESTNET_PRIVATE_KEY`
5. Opt-in to ASA to receive assets on Algorand

### Execute Transfer

Run the below command to transfer from evm to avm:

```bash
npx env-cmd tsx examples/transfer-evm-to-avm-custom-token.ts
```

Alternatively, you could use below command to transfer from avm to evm:

```bash
npx env-cmd tsx examples/transfer-avm-to-evm-custom-token.ts
```

## Monitor Transfer Status

### Via WormholeScan

Visit: `https://wormholescan.io/#/tx/{YOUR_TX_HASH}?network=Testnet`

**Transfer Stages:**

1. **Source Finalized** \- Transaction confirmed on Source Chain
2. **Guardian Signed** \- Wormhole guardians created VAA
3. **Transfer Finalized** \- Transfer tokens to Destination Chain
4. **Successful** \- Tokens transferred on Destination Chain to user account

On completion the transaction status will change to `successful`. You should now be able to see the tokens being received in the destination account address.

Figure: AVM to EVM transfer result

Figure: EVM to AVM transfer result

## Troubleshooting Guide

### Useful Commands Reference

| Operation | Command |
| --- | --- |
| Mint Tokens | `cast send $TOKEN "mint(address,uint256)" $RECIPIENT $AMOUNT --rpc-url $RPC --private-key $KEY` |
| Check token balance | `cast call $TOKEN "balanceOf(address)(uint256)" $ADDRESS --rpc-url $RPC` |
| Approve spender | `cast send $TOKEN "approve(address,uint256)" $SPENDER $AMOUNT --rpc-url $RPC --private-key $KEY` |
| Check allowance | `cast call $TOKEN "allowance(address,address)(uint256)" $OWNER $SPENDER --rpc-url $RPC` |
| Check decimals | `cast call $TOKEN "decimals()(uint8)" --rpc-url $RPC` |
| Check total supply | `cast call $TOKEN "totalSupply()(uint256)" --rpc-url $RPC` |
| Check ETH balance | `cast balance $ADDRESS --rpc-url $RPC` |
| Send ETH | `cast send $RECIPIENT --value 0.1ether --rpc-url $RPC --private-key $KEY` |
| Get nonce | `cast nonce $ADDRESS --rpc-url $RPC` |
| Get transaction receipt | `cast receipt $TX_HASH --rpc-url $RPC` |
| Get peer configuration | `cast call $WORMHOLE_TRANSCEIVER "getWormholePeer(uint16)(bytes32)" $CHAIN_ID --rpc-url $RPC` |
| Check if peer is set | `cast call $NTT_MANAGER "getPeer(uint16)((bytes32,uint8,uint256))" $CHAIN_ID --rpc-url $RPC` |
| Get rate limit info | `cast call $NTT_MANAGER "getCurrentOutboundCapacity()(uint256)" --rpc-url $RPC \ cast call $NTT_MANAGER "getCurrentInboundCapacity(uint16)(uint256)" $CHAIN_ID --rpc-url $RPC` |
| ETH balance | `cast balance $YOUR_EVM_ADDRESS --rpc-url https://sepolia.base.org` |
| Token balance | `cast call $TOKEN_ADDRESS "balanceOf(address)(uint256)" $YOUR_EVM_ADDRESS --rpc-url https://sepolia.base.org` |
| Token allowance for FolksBridge | `cast call $TOKEN_ADDRESS "allowance(address,address)(uint256)" $YOUR_EVM_ADDRESS $BASE_SEPOLIA_NTT_EXECUTOR_ADDRESS  --rpc-url https://sepolia.base.org` |
| Check WormholeTransceiver peer on Base Sepolia | ` cast call $WORMHOLE_TRANSCEIVER_ADDRESS "getWormholePeer(uint16)(bytes32)"   8  --rpc-url https://sepolia.base.org` |

### Troubleshooting Decision Tree

```bash
Transfer Failed?

│

├─> Insufficient funds error?

|   ├─> Check source chain

|     ├─> Algorand: Check ALGO balance(need 10 ALGO)

|         ├─> Get funds from [Lora](https://lora.algokit.io/testnet/fund)

│     ├─> ETH: Check ETH balance (need 0.002+ ETH)

│         └─> Get funds from Coinbase or any other faucet

│

├─> InsufficientAllowance (0xfb8f41b2)?

│   ├─> Approve FolksBridge Contract

│   └─> Approve NttManager Address

│

├─> InsufficientBalance (0xe450d38c)?

│   ├─> Check token balance

│   ├─> Mint/Transfer more tokens if needed

│   └─> Use ETH/ALGO for fees instead of FOLKS

│

├─> Invalid private key?

│   ├─> Ensure 0x prefix

│   └─> Verify 64 hex characters

│

├─> Decimal mismatch?

│   ├─> Check both chains have same decimals

│   └─> Override decimals() in PeerToken.sol

│

├─> Peer not configured?

│   ├─> Verify setPeer on both chains

│   ├─> Check 32-byte padding for EVM addresses

│   └─> Run configure.ts on Algorand

│

├─> VAA not delivered?

|   ├─> Transaction successful on EVM chain?

|   └─> Check WormholeScan for VAA Status

|

├─> Transfer Got Stuck at Finalised?

|   |-> NTT Address has minter role?

|   └─> Grant minter role to NTTManager
```

## Conclusion

You have successfully deployed a cross-chain NTT token bridge between Base Sepolia and Algorand Testnet. Once you are confident with the bidirectional transfers on testnet you can start preparing for mainnet deployment with updated configurations

## Resources

- [Wormhole NTT Framework](https://docs.wormhole.com/wormhole/tutorials/ntt-quickstart)
- [Wormhole NTT](https://github.com/wormhole-foundation/native-token-transfers)
- [Folks Finance Algorand NTT](https://github.com/Folks-Finance/algorand-ntt-contracts)
- [Folks Finance SDK](https://github.com/Folks-Finance/wormhole-ntt-sdk)
- [EVM Token Example](https://github.com/wormhole-foundation/example-ntt-token-evm)

#### Network Tools

- [WormholeScan Testnet](https://wormholescan.io/#/?network=Testnet)
- [Base Sepolia Explorer](https://sepolia.basescan.org/)
- [Lora Testnet](https://lora.algokit.io/)

#### Faucets

- [Base Sepolia (Coinbase)](https://www.coinbase.com/faucets/base-ethereum-sepolia-faucet)
- [Base Sepolia (Alchemy)](https://www.alchemy.com/faucets/base-sepolia)
- [Algorand Testnet](https://lora.algokit.io/testnet/fund)
