Integrating with blockchains
Introduction
NOUMENA Cloud supports integration with blockchain networks through a dedicated Blockchain Connector service. The Blockchain Connector allows you to create and fund wallets, deploy contracts and call functions on contracts. This guide will help you set up the Blockchain Connector and integrate it with your NOUMENA Cloud application.
This tutorial references the npl-blockchain-starter repository, which contains a complete working example demonstrating blockchain integration with NPL Runtime. It includes a React frontend, smart contracts for both EVM and Stellar blockchains, and comprehensive NPL code for wallet management and token operations. You can use this repository as a template for your own blockchain projects.
The blockchain connector is a service that connects NPL notifications with blockchain services. Each message recognized by the blockchain service is processed, translated into the right blockchain transaction, signed and executed, before performing a callback on the engine. Transactions are signed by wallets whose private keys are stored in the blockchain connector. A faucet wallet can be specified in the NOUMENA Cloud Portal to fund wallets on creation or as an individual action.
Lifecycle of blockchain actions
In general, blockchain networks require transactions to be paid for by the wallet that emits the transaction.
Wallets need funds to issue transactions. The blockchain connector requires the wallet private key to be able to emit and sign transactions. There are two ways to get wallets into the blockchain connector:
- Register as faucet wallet: A faucet wallet is a pre-loaded wallet that will send tokens to other wallets upon their creation, or on request. Created wallets can then pay for their own transactions. A single faucet wallet can be registered per blockchain network. The private key is required for the system to independently perform faucet funding transactions.
- Create a wallet with the create wallet function: All wallets created this way are stored in the blockchain connector. After creating the wallet, the blockchain connector returns the public address of the wallet (never the private key), which needs to be stored to be used in the application. This address can be used as a recipient address for a transaction coming from outside the system.
This wallet can then be used to deploy contracts, call functions, and fund other wallets. After a contract deployment, the blockchain connector returns the contract address, which needs to be stored to be used in the application. It can then be used as a reference on-chain, or functions can be called on this contract.
Additional wallets can be created without requiring payment if a faucet wallet is configured.
Asynchronous transaction processing
From NPL, to send a transaction to the blockchain connector requires emitting a notification. Notifications are processed in order and may be submitted to the blockchain network in parallel. A callback permission is asynchronously called in NPL once the transaction is complete. This implies that while transaction requests are submitted in order to the blockchain connector, the completion of the blockchain transaction and the calling of the callback function do not need to take place in order.
Typical application lifecycle
A typical application lifecycle with the blockchain connector follows these steps:
Wallet creation and funding
- NPL requests a wallet creation with a notification and specifies the callback permission for the operation
- Transaction is submitted and the callback permission is called in NPL
- NPL stores the wallet address and shows it to the user. The app can display a link to the blockchain explorer for the wallet
- The user sends tokens (used for transaction fees) to the shown address, or the faucet wallet automatically funds it. The wallet can now emit transactions
- Additional wallets can be created and funded in the same way, or funded from existing wallets
Token Transfers
- NPL can initiate token transfers between wallets by calling the appropriate smart contract function
- Transaction is submitted and the callback permission is called in NPL with the transaction hash
Contract Deployment
- NPL requests a contract deployment with a notification and specifies the callback permission for the operation
- Transaction is submitted and the callback permission is called in NPL
- NPL stores the contract address. The app can display a link to the blockchain explorer for the contract
- Additional contracts can be deployed following the same pattern
Function Calls on Contracts
- NPL invokes a function on the deployed contract with a notification, specifying the function name, arguments, and callback permission
- Transaction is submitted to the blockchain network
- The callback permission is called in NPL with the transaction result
- NPL processes the result and updates the application state accordingly
- Additional function calls can be made on deployed contracts as needed
Getting started
To get started with the Blockchain Connector, you need to configure the service to connect to selected blockchain services. The configuration includes:
- Selecting blockchain networks (EVM, Stellar)
- Specifying URLs for blockchain nodes and API services
- Providing an archive of compiled smart contracts that will be deployed upon NPL notification
- Optionally specifying a faucet wallet to perform automatic funding operations
Once configured, you can enable the blockchain connector. You will also need to implement the necessary logic in NPL to send notifications to the Blockchain Connector.
Example: Blockchain Offering Flow
The following sequence diagram illustrates the flow of an NPL application using the Blockchain Connector to create a blockchain offering. It shows the steps involved in creating an offering on the blockchain:
- Create a wallet for the issuer
- Deploy a smart contract (e.g., a token contract)
- Mint tokens to the issuer's wallet
- Whitelist the investor wallets
- Transfer tokens to the investor wallets
System Architecture
The blockchain connector integrates with the NOUMENA Cloud architecture as follows:
- NOUMENA Cloud: The main platform hosting your application
- Services:
- Engine: Executes NPL protocol logic and manages state
- AMQP: Message broker for asynchronous communication (required for blockchain connector)
- Blockchain Connector: Translates NPL notifications into blockchain transactions
- Blockchain Networks: External blockchain networks (EVM, Stellar)
- Authentication & Party Model: Keycloak manages user authentication and party identities
- Blockchain Wallets: Securely stored in the blockchain connector's vault, linked to parties in your NPL application
Architecture Flow
The blockchain connector integrates seamlessly with NOUMENA Cloud's microservices architecture through an event-driven messaging pattern:
@startuml
!define RECTANGLE class
skinparam rectangle {
BackgroundColor<<noumena>> LightBlue
BackgroundColor<<service>> LightYellow
BackgroundColor<<external>> LightGreen
BackgroundColor<<security>> LightCoral
BorderColor Black
}
rectangle "NOUMENA Cloud" <<noumena>> {
rectangle "UI" <<service>> as UI
rectangle "Engine\n(NPL)" <<service>> as Engine
rectangle "AMQP\n(Message Broker)" <<service>> as AMQP
rectangle "Blockchain Connector" <<service>> as BC {
note right
• Process events
• Sign transactions
• Manage wallets
end note
}
rectangle "Identity & Access Management\n(IAM / Keycloak)" <<security>> as IAM {
note right
• Party model (claims)
• Authentication
• Authorization
end note
}
rectangle "Blockchain Wallet Vault" <<security>> as Vault {
note right
• Secure key storage
• Wallet management
• Transaction signing
end note
}
}
rectangle "Blockchain Networks" <<external>> as Blockchain {
note right
• EVM (Ethereum, etc.)
• Stellar
end note
}
UI -down-> Engine : user actions
Engine -down-> AMQP : "① notify event"
AMQP -down-> BC : "② route message"
BC -up-> Engine : "③ callback"
BC -down-> Blockchain : "submit transaction"
BC ..> IAM : "uses claims/party"
BC ..> Vault : "retrieves keys"
@enduml
Message Flow:
- ① Notify Event: The NPL Engine emits blockchain operation requests as notifications through AMQP
- ② Route Message: AMQP routes the message to the Blockchain Connector based on the message type
- ③ Callback: The Blockchain Connector performs an asynchronous callback to the Engine with the transaction result
Component Responsibilities:
- UI: User interface for interacting with the application
- Engine (NPL): Executes protocol logic, manages application state, and orchestrates blockchain operations
- AMQP: Message broker enabling asynchronous communication between Engine and connectors
- Blockchain Connector: Translates NPL notifications into blockchain transactions, manages transaction lifecycle
- IAM (Keycloak): Manages user authentication and party identities with claims-based authorization
- Blockchain Wallet Vault: Securely stores private keys for wallets
- Blockchain Networks: External blockchain networks (EVM-compatible chains, Stellar) where transactions are executed
Security Model:
The architecture ensures secure blockchain integration by:
- Isolating private keys in the blockchain connector's vault (never exposed to Engine or UI)
- Using claims-based authorization through IAM to validate callbacks
- Mapping blockchain wallets to authenticated parties in the system
- Processing all blockchain operations asynchronously to prevent blocking
Dependencies
NPL connectors library
The npl-connectors-library
needs to be added as a dependency to the NPL project. See the Contributor library documentation for more information.
<!-- pom.xml -->
<!-- add dependency -->
<dependencies>
<!--...-->
<dependency>
<groupId>com.noumenadigital.contrib</groupId>
<artifactId>npl-connectors-library</artifactId>
<version>1.0.19</version>
</dependency>
</dependencies>
<!-- unpack library -->
<build>
<plugins>
<!--...-->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<executions>
<execution>
<id>copy-npl-contrib</id>
<goals>
<goal>copy-dependencies</goal>
</goals>
<phase>generate-sources</phase>
<configuration>
<outputDirectory>${project.build.directory}/contrib</outputDirectory>
<includeGroupIds>com.noumenadigital.contrib</includeGroupIds>
<includeArtifactIds>npl-connectors-library</includeArtifactIds>
<overWriteIfNewer>true</overWriteIfNewer>
</configuration>
</execution>
<execution>
<id>unzip-contrib-libs</id>
<goals>
<goal>unpack-dependencies</goal>
</goals>
<phase>generate-sources</phase>
<configuration>
<outputDirectory>${project.build.directory}/classes</outputDirectory>
<includeArtifactIds>npl-connectors-library</includeArtifactIds>
<fileMappers>
<fileMapper implementation="org.codehaus.plexus.components.io.filemappers.RegExpFileMapper">
<pattern>npl-connectors-library-.*/connector</pattern>
<replacement>connector</replacement>
</fileMapper>
</fileMappers>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
Stellar dependencies
For developing Stellar smart contracts (Soroban), you'll need:
- Rust toolchain: Required for compiling Stellar smart contracts to WebAssembly (WASM)
# Install Rust if not already installed
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
# Reload terminal, then add the WebAssembly target
rustup target add wasm32-unknown-unknown
- Stellar CLI: Command-line tool for building, optimizing, and deploying Stellar smart contracts
cargo install --locked stellar-cli --features opt
Project Structure: Stellar/Soroban projects use a workspace structure with individual contracts in contracts/ subdirectories. Each contract has its own Cargo.toml that relies on the workspace Cargo.toml for dependencies. See the npl-blockchain-starter stellar directory for an example structure.
EVM dependencies
For developing EVM (Ethereum Virtual Machine) smart contracts with Solidity, you'll need:
-
Node.js and npm: For running Hardhat and other Ethereum development tools. Install via:
-
macOS:
brew install node - Windows: Download from nodejs.org or use
choco install nodejs -
Verify installation:
node -v && npm -v -
Hardhat: Ethereum development environment for compiling, testing, and deploying smart contracts
# In your project directory
npm install --save-dev hardhat @nomicfoundation/hardhat-toolbox
- Solidity compiler: Automatically managed by Hardhat when you compile contracts with
npx hardhat compile
Project Structure: EVM projects typically have a contracts/ directory for .sol files, a hardhat.config.js for configuration, and compiled artifacts in artifacts/. See the npl-blockchain-starter solidity directory for a complete example.
Configuring the Blockchain Connector
Blockchain networks
Blockchain Connector can be enabled in your NOUMENA Cloud application by navigating to the services section in the NOUMENA Cloud portal. Configure the blockchain connector by selecting network types and specifying configurations and an archive containing smart contracts for the respective blockchains. Once the blockchain connector is configured, the service can be enabled.
NOUMENA Cloud supports EVM and Stellar blockchains.
EVM
To configure an EVM-compatible blockchain network, you need to provide:
- Faucet wallet private key: The private key of a wallet that already has funds. This wallet will be used to automatically fund newly created wallets. Keep this secure as it controls a funded wallet. Private keys are stored in Vault (secure key storage).
- RPC URL: The Remote Procedure Call endpoint URL for connecting to the EVM blockchain node. This can be:
- A public RPC endpoint (e.g.,
https://mainnet.infura.io/v3/YOUR_API_KEYfor Ethereum) - A private node endpoint
- A testnet endpoint (e.g.,
https://sepolia.infura.io/v3/YOUR_API_KEYfor Sepolia testnet)
Stellar
To configure a Stellar blockchain network, you need to provide:
- Network: The Stellar network to connect to:
TESTNET- For testing and development (uses test XLM with no real value)PUBLIC- The main Stellar network (uses real XLM)FUTURENET- For testing upcoming Stellar features- Faucet wallet secret key: The secret key of a Stellar wallet that already has XLM. This wallet will be used to automatically fund newly created wallets with XLM for transaction fees.
- RPC URL: The Soroban RPC endpoint URL for interacting with Stellar smart contracts (Soroban):
- Testnet:
https://soroban-testnet.stellar.org - Mainnet:
https://soroban-mainnet.stellar.org - Horizon API URL: The Horizon API endpoint for querying Stellar network data and submitting transactions:
- Testnet:
https://horizon-testnet.stellar.org - Mainnet:
https://horizon.stellar.org
Stellar Native Assets
Stellar has a unique feature called native assets that allows you to create and transfer custom tokens directly on the Stellar network without deploying a smart contract. This is different from EVM chains where you typically need to deploy an ERC-20 contract.
When to Use Stellar Native Assets
- Simple token transfers: When you only need basic token functionality (issue, transfer, burn)
- Lower costs: Native assets don't require smart contract deployment fees
- Established trust lines: When leveraging Stellar's built-in trust line system
- Compliance features: When you need Stellar's built-in authorization controls
When to Use Stellar Smart Contracts (Soroban)
- Complex token logic: When you need custom business logic, advanced features, or compliance rules
- DeFi applications: For building decentralized exchanges, lending protocols, etc.
- Cross-contract interactions: When your token needs to interact with other smart contracts
- Custom access control: For sophisticated permission and governance systems
Using Native Assets with the Blockchain Connector
While the blockchain connector primarily focuses on smart contract interactions, you can interact with Stellar native assets through custom smart contracts that wrap or interact with native asset operations. For direct native asset operations, you may need to use Stellar's SDK directly or extend the blockchain connector functionality.
Smart contract
Creating the smart contract zip file
The blockchain connector can deploy smart contracts on-chain. NOUMENA Cloud allows you to upload compiled smart contracts through the NOUMENA Cloud Portal. The blockchain connector service stores these smart contracts and deploys them upon deployment request from NPL.
Contract Archive Lifecycle: You can update the contract archive at any time by specifying a new zip file. This replaces the previous archive. You can add new contracts or update existing ones. However, contracts already deployed on the blockchain cannot be modified (blockchain immutability), but you can deploy new versions with different names or addresses.
Using npl-blockchain-starter
The easiest way to create a contract archive is using the npl-blockchain-starter repository.
Setup Prerequisites:
- Clone the repository:
git clone https://github.com/NoumenaDigital/npl-blockchain-starter.git
cd npl-blockchain-starter
-
Install dependencies:
-
For Solidity contracts (EVM):
cd blockchain/solidity npm install cd ../.. -
For Stellar contracts (requires Rust):
# Install Rust if not already installed curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh # Reload terminal, then add the WebAssembly target rustup target add wasm32-unknown-unknown # Install Stellar CLI cargo install --locked stellar-cli --features opt -
Compile and create the archive:
From the root of the project, run:
make blockchain-zip
This command will:
- Compile all Solidity contracts using Hardhat (located in
blockchain/solidity/contracts/) - Build all Stellar contracts using the Stellar CLI (located in
blockchain/stellar/contracts/) - Create a properly structured ZIP archive in
blockchain-tmp/compiled-smart-contracts.zipready for upload
Manual Creation
If you prefer to create the archive manually:
- Create the directory structure:
mkdir -p blockchain-tmp/solidity blockchain-tmp/stellar
- Compile Solidity contracts (EVM):
npx hardhat compile
# Compiled artifacts will be in artifacts/contracts/
# Copy the JSON files to blockchain-tmp/solidity/
cp artifacts/contracts/YourContract.sol/YourContract.json blockchain-tmp/solidity/
- Build Stellar contracts (Soroban):
stellar contract build
# This creates a .wasm file in target/wasm32-unknown-unknown/release/
cp target/wasm32-unknown-unknown/release/your_contract.wasm blockchain-tmp/stellar/
If needed, optimize the Stellar contract:
stellar contract optimize --wasm target/wasm32-unknown-unknown/release/your_contract.wasm
cp target/wasm32-unknown-unknown/release/your_contract.optimized.wasm blockchain-tmp/stellar/YourContract.wasm
- Create the ZIP archive:
cd blockchain-tmp zip -r compiled-smart-contracts.zip solidity stellar
Expected file structure:
blockchain-tmp/
├── compiled-smart-contracts.zip
├── solidity/
│ ├── ExampleToken.json
│ └── AnotherContract.json
└── stellar/
├── ExampleToken.wasm
└── AnotherContract.wasm
Important Notes:
- Solidity contracts must be in JSON format (Hardhat artifact format)
- Stellar contracts must be in WASM format
- File names in the archive will be used as contract identifiers when deploying from NPL
- You only need to include directories for the blockchain types you're using (e.g., only
stellar/if not using EVM)
Uploading the smart contract zip file
To upload a smart contract archive, specify the URL of the archive and the associated authorization header. The blockchain connector will download the archive from this URL when it starts.
To update the archive, update the URL, save the configuration, then deactivate and reactivate the blockchain connector service.
Configuration fields:
-
URL to the smart contract archive (ZIP file): A publicly accessible URL where your compiled smart contracts ZIP file is hosted. Options include:
-
GitHub releases (e.g.,
https://api.github.com/repos/your-org/your-repo/releases/assets/your-asset-number) - Cloud storage (AWS S3, Azure Blob Storage, Google Cloud Storage)
- Your own web server or CDN
-
Artifact repositories (e.g., Nexus, Artifactory)
-
Authorization header: If the URL requires authentication, provide the authorization header value. For example:
- Bearer token:
Bearer your-token-here - Basic auth:
Basic base64-encoded-credentials - Custom headers: Provide the full header value as needed by your hosting service
Example: Using GitHub Releases
- Create a release in your GitHub repository
- Upload your
compiled-smart-contracts.zipas a release asset - Copy the download URL (e.g.,
https://github.com/NoumenaDigital/npl-blockchain-starter/releases/download/v1.0.0/compiled-smart-contracts.zip) - Paste the URL in the NOUMENA Cloud Portal
- Leave the Authorization header empty (GitHub releases are public)
Example: Using Cloud Storage with Authentication
- Upload your ZIP file to your cloud storage (e.g., AWS S3 bucket)
- Generate a presigned URL or configure access with an API key
- Enter the URL in the NOUMENA Cloud Portal
- If needed, add the authorization header (e.g.,
Bearer YOUR_API_KEY)
Best Practices:
- Use versioned URLs (e.g., include version numbers in the URL or use tagged releases)
- Ensure the URL is accessible from the NOUMENA Cloud environment
- Test the URL in a browser or with
curlbefore adding it to the portal - Update the URL in the portal whenever you deploy new contract versions
Using the Blockchain Connector in NPL
Once the Blockchain Connector is configured, you can use it in your NPL applications. The Blockchain Connector provides a set of APIs that can be used to interact with the blockchain using the NPL notify-resume pattern.
Message Types
The blockchain connector provides 4 main types of messages:
WalletCreateMessage: Creates a new wallet on the blockchain. The wallet's private key is securely stored in the blockchain connector's vault and never exposed.WalletFaucetFundMessage: Funds a wallet with tokens from the configured faucet wallet. The faucet wallet is the pre-funded wallet you configured in the portal.BlockchainDeployMessage: Deploys a smart contract from your uploaded archive to the blockchain.BlockchainFunctionMessage: Invokes a function on a deployed smart contract.
Asynchronous Processing
All blockchain operations are asynchronous:
- NPL sends a notification to the blockchain connector
- The blockchain connector processes the request and submits it to the blockchain
- Once the blockchain transaction completes, the connector calls back to NPL via the specified resume permission
- Your NPL code handles the result (success or failure) in the callback function
Creating a wallet
Creating a wallet is typically the first step in integrating blockchain functionality into your application.
Use case: When a new user joins your platform, you can automatically create a blockchain wallet for them. The wallet is securely managed by the blockchain connector, and the user never needs to handle private keys directly. This is ideal for applications where you want to abstract blockchain complexity from end users.
Understanding the callback responses:
NotifySuccesswithBlockchainStatus.Success: Wallet created successfully. The response contains the wallet address.NotifySuccesswithBlockchainStatus.Failure: Blockchain operation failed (e.g., insufficient faucet funds, network error). The response contains error details.NotifyFailure: Internal error between NPL and the blockchain connector (rare). This indicates a communication or configuration issue.
What happens with the created wallet?:
- The private key is securely stored in the blockchain connector's vault
- The public address is returned to NPL and should be stored in a protocol field
- The wallet can be used for deploying contracts, calling functions, and transferring tokens
- If
faucetFundis specified, the faucet wallet automatically sends funds for gas fees
permission[issuer] createWallet() {
// Configure wallet creation parameters
var args = WalletCreate(
storageType = WalletStorageType.Vault, // Store wallet in secure vault
walletType = WalletType.Stellar, // Create a Stellar wallet
faucetFund = optionalOf(0) // Optional: fund wallet from faucet
);
// Send notification to Blockchain Connector and wait for response
notify WalletCreateMessage(args) resume createWalletCallback;
}
@api
permission[connector] createWalletCallback(res: NotifyResult<WalletCreateResponse>) {
match(res) {
is NotifySuccess<WalletCreateResponse> -> match(res.result.status) {
// Wallet created successfully - extract the wallet address
BlockchainStatus.Success -> {
var walletAddress = res.result.walletAddress.getOrFail();
// Store the wallet address in your protocol state for future use
// Display the address to users or link to blockchain explorer
// ...
}
// Wallet creation failed - extract error details
BlockchainStatus.Failure -> {
var failureDetails = res.result.details.getOrFail();
// Handle error: log details, notify user, retry if appropriate
// ...
}
}
// The blockchain response, including errors, is passed via NotifySuccess.
// This notify failure is called in case there is an internal error between NPL and the blockchain connector.
is NotifyFailure -> { }
};
};
Funding a wallet
Funding a wallet provides it with the native tokens needed to pay for transaction fees (gas).
Use case: After creating a wallet, you may need to fund it so it can perform transactions. This is useful when:
- A newly created wallet needs initial funds to deploy contracts or call functions
- A wallet has run out of funds and needs to be topped up
- You want to distribute tokens from a central treasury to user wallets
The faucet wallet you configured in the portal is used as the source of funds.
permission[issuer] fundWallet(address: Text) {
// Configure faucet funding parameters
var args = WalletFaucetFund(
address = address, // Wallet address to fund
amount = 10, // Amount of tokens to fund
blockchainType = BlockchainType.Stellar // Blockchain network
);
// Request faucet funding from Blockchain Connector
notify WalletFaucetFundMessage(args) resume fundWalletCallback;
};
@api
permission[connector] fundWalletCallback(res: NotifyResult<WalletFaucetFundResponse>) {
match (res) {
is NotifySuccess<WalletFaucetFundResponse> -> match (res.result.status) {
// Funding successful - extract transaction hash
BlockchainStatus.Success -> {
var txHash = res.result.hash.getOrFail();
// Wallet now has funds and can perform transactions
// ...
}
// Funding failed - extract error details
BlockchainStatus.Failure -> {
var failureDetails = res.result.details.getOrFail();
// Handle error: insufficient faucet balance, network issues, etc.
// ...
}
}
// The blockchain response, including errors, is passed via NotifySuccess.
// This notify failure is called in case there is an internal error between NPL and the blockchain connector.
is NotifyFailure -> {}
};
};
Deploying a smart contract
Deploying a smart contract publishes your contract code to the blockchain, making it available for interaction.
Use case: Deploy smart contracts for various purposes:
- Token contracts (ERC-20, custom tokens)
- NFT contracts (ERC-721, ERC-1155)
- DeFi protocols (lending, swapping, staking)
- Custom business logic (escrow, voting, supply chain tracking)
Contract path specification: You must specify the path to the compiled contract within your uploaded archive:
- For Stellar contracts:
stellar/YourContract.wasm(WASM file) - For EVM contracts:
solidity/YourContract.json(Hardhat artifact JSON file)
The path should match the file structure in your ZIP archive exactly.
Constructor arguments: The args parameter contains the initialization values for your smart contract. These are passed to the contract's constructor.
permission[issuer] deployContract(senderAddress: Text) {
// Configure contract deployment parameters
var args = BlockchainDeploy(
senderAddress = senderAddress, // Wallet address that deploys the contract
compiledContractPath = "stellar/token.wasm", // Path to compiled contract in archive
blockchainType = BlockchainType.Stellar, // Target blockchain
args = [ // Constructor arguments for the contract
Address(senderAddress), // Admin address
Text("MyToken"), // Token name
Text("MTK") // Token symbol
]
);
// Deploy contract via Blockchain Connector
notify BlockchainDeployMessage(args) resume deploymentCallback;
};
@api
permission[connector] deploymentCallback(res: NotifyResult<BlockchainDeployResponse>) {
match(res) {
is NotifySuccess<BlockchainDeployResponse> -> match(res.result.status) {
// Deployment successful - extract the deployed contract address
BlockchainStatus.Success -> {
var contractAddress = res.result.contractAddress.getOrFail();
// Store contract address for future function calls
// Display to users or link to blockchain explorer
// ...
}
// Deployment failed - extract error details
BlockchainStatus.Failure -> {
var failureDetails = res.result.details.getOrFail();
// Handle error: insufficient gas, invalid contract, network issues
// ...
}
}
// The blockchain response, including errors, is passed via NotifySuccess.
// This notify failure is called in case there is an internal error between NPL and the blockchain connector.
is NotifyFailure -> { }
};
};
Invoking functions on the smart contract
Once a contract is deployed, you can call its functions to interact with it.
Use case: Call smart contract functions for various operations:
- Token operations: mint tokens, transfer tokens, burn tokens, approve spending
- Access control: grant roles, revoke roles, check permissions
- Data management: store data on-chain, retrieve data, update records
- Business logic: execute escrow releases, record votes, update state
Function arguments: The args parameter contains the arguments to pass to the contract function. Common argument types include:
- Addresses (wallet or contract addresses)
- Amounts (token balances, wei values)
- Strings (names, symbols, metadata)
- Numbers (IDs, timestamps, quantities)
- Booleans (flags, state indicators)
permission[issuer] mintTokens(contractAddress: Text, senderAddress: Text, amount: Int64) {
// Configure function call parameters
var args = BlockchainFunction(
functionName = "mint", // Name of the contract function to call
contractAddress = contractAddress, // Address of the deployed contract
senderAddress = senderAddress, // Wallet address making the call
blockchainType = BlockchainType.Stellar, // Blockchain network
args = [ // Function arguments
Address(senderAddress), // Recipient address
TokenBalance(amount) // Number of tokens to mint
]
);
// Invoke contract function via Blockchain Connector
notify BlockchainFunctionMessage(args) resume mintingCallback;
}
@api
permission[connector] mintingCallback(res: NotifyResult<BlockchainFunctionResponse>) {
match(res) {
is NotifySuccess<BlockchainFunctionResponse> -> match(res.result.status) {
// Function call successful - extract transaction hash
BlockchainStatus.Success -> {
var txHash = res.result.hash.getOrFail();
// Store transaction hash, update protocol state, notify users
// ...
}
// Function call failed - extract error details
BlockchainStatus.Failure -> {
var failureDetails = res.result.details.getOrFail();
// Handle error: reverted transaction, insufficient permissions, invalid args
// ...
}
}
// The blockchain response, including errors, is passed via NotifySuccess.
// This notify failure is called in case there is an internal error between NPL and the blockchain connector.
is NotifyFailure -> { }
};
};
Supported Argument Types
The following types are supported for smart contract function arguments and constructor parameters:
union ArgData {
Text,
Boolean,
Int8,
Int16,
Int32,
Int64,
Int128,
Int256,
Uint8,
Uint16,
Uint32,
Uint64,
Uint128,
Uint256,
Address,
TokenBalance /* EVM: uint256, Stellar: i128 */,
TokenDecimal /* EVM: uint8, Stellar: u32 */,
Bytes32,
List<ArgData>
};
Type Mapping Notes:
Address: Blockchain address (Ethereum address or Stellar account ID)TokenBalance: Large integer for token amounts (maps to uint256 on EVM, i128 on Stellar)TokenDecimal: Decimal precision for tokens (uint8 on EVM, u32 on Stellar)Bytes32: Fixed-size byte array (commonly used for hashes and identifiers)List<ArgData>: Arrays of any supported type
Best Practices
Error Handling
Always handle both success and failure cases in your callback functions:
@api
permission[connector] callback(res: NotifyResult<Response>) {
match(res) {
is NotifySuccess<Response> -> match(res.result.status) {
BlockchainStatus.Success -> {
// Success path: update state, notify users
}
BlockchainStatus.Failure -> {
// Blockchain error: log, notify, possibly retry
var error = res.result.details.getOrFail();
error("Blockchain operation failed: " + error);
}
}
is NotifyFailure -> {
// System error: alert administrators
error("Critical: blockchain connector communication failure");
}
};
}
State Management
Store blockchain-related data in your protocol state:
state {
// Store wallet addresses mapped to parties
walletAddresses: Map<Party, Text>,
// Store deployed contract addresses
tokenContractAddress: Optional<Text>,
// Track transaction hashes for audit trail
transactionHistory: List<Text>
}
Security Considerations
- Never expose private keys: Private keys are securely stored in the blockchain connector's vault
- Validate inputs: Always validate addresses and amounts before sending to blockchain
- Handle failed transactions: Implement proper error handling and consider retry logic for transient failures
- Monitor faucet balance: Ensure your faucet wallet has sufficient funds for ongoing operations
- Use testnet first: Test your integration on testnet before deploying to mainnet
Example Application Architecture
The npl-blockchain-starter repository demonstrates a complete blockchain integration with the following components:
Frontend (React)
- Location:
frontend/directory - Features: Wallet management dashboard, token contract deployment, transaction monitoring
- API Generation: Uses OpenAPI to generate TypeScript client from NPL definitions
cd npl && mvn clean package # Generate OpenAPI spec cd ../frontend && npm run generate-client # Generate TypeScript client - Deployment Options:
- NOUMENA Cloud: Build with
npm run buildand deploy thedist/folder - Local Development: Run
npm run devwith Vite proxy for CORS-free development
NPL Runtime
- Location:
npl/directory - NPL Code: Protocols for wallet creation, contract deployment, token minting and transfers
- Integration: Uses
npl-connectors-librarydependency for blockchain message types - Deployment: Package with
mvn clean packageand upload to NOUMENA Cloud
Smart Contracts
- Solidity (EVM):
blockchain/solidity/contracts/ExampleToken.sol - ERC-20 compatible token contract
- Compile with:
cd blockchain/solidity && npx hardhat compile - Stellar (Soroban):
blockchain/stellar/contracts/ExampleToken/ - Rust-based smart contract
- Build with:
stellar contract build(from stellar directory)
Application Flow
The starter demonstrates a complete token issuance workflow:
- Create application wallet via NPL notification
- Fund wallet from external faucet (MetaMask for EVM, Stellar CLI for Stellar)
- Deploy custom token contract from NPL
- Create customer wallets
- Distribute tokens to customer wallets
- Transfer tokens between wallets
For detailed setup instructions, architecture diagrams, and deployment guides, see the npl-blockchain-starter README.
Additional Resources
- npl-blockchain-starter repository: Complete working example with React frontend, smart contracts, and NPL integration
- NPL Documentation: Complete NPL language reference
- AMQP Integration Guide: Setting up the AMQP broker
- Contributor Library: Using npl-connectors-library for blockchain message types





