
Research
Supply Chain Attack on Axios Pulls Malicious Dependency from npm
A supply chain attack on Axios introduced a malicious dependency, plain-crypto-js@4.2.1, published minutes earlier and absent from the project’s GitHub releases.
@console-wallet/dapp-sdk
Advanced tools
Console DApp SDK allows dApps to connect to a Console Wallet account. The Console wallet is a browser extension. All interaction happens inside the dApp. For signing, users will be prompted to sign in their Console Wallet browser extension.
Console DApp SDK allows dApps to connect to a Console Wallet account. The Console wallet is a browser extension. All interaction happens inside the dApp. For signing, users will be prompted to sign in their Console Wallet browser extension.
This SDK is compatible with the CIP-0103 standard.
To use the Console DApp SDK, you first need to install it from NPM:
npm install @console-wallet/dapp-sdk
# or
yarn add @console-wallet/dapp-sdk
Note that, if you don't want to implement a build process, you can include the file directly with unpkg:
<script type="module">
import { consoleWalletPixelplex } from 'https://unpkg.com/@console-wallet/dapp-sdk@latest/dist/esm/index.js';
</script>
import { consoleWalletPixelplex } from '@console-wallet/dapp-sdk';
Before attempting to connect, check if the Console Wallet extension is installed and capable by version:
const checkWallet = async () => {
try {
const availability = await consoleWalletPixelplex.checkExtensionAvailability();
if (availability.status === 'installed') {
console.log('Console Wallet is installed');
} else {
console.log('Console Wallet is not installed');
// Show installation instructions to the user
}
if (availability.isExtensionCapableByVersion) {
console.log('Console Wallet is capable by version');
} else {
console.log('Console Wallet is not capable by version');
}
} catch (error) {
console.error('Error checking wallet availability:', error);
}
};
You can check the current connection status at any time:
const checkStatus = async () => {
try {
const status = await consoleWalletPixelplex.status();
console.log('Connection status:', status.isConnected); // 'connected' or 'disconnected'
} catch (error) {
console.error('Error checking status:', error);
}
};
To initiate the connection, call connect() with optional dApp metadata:
const handleConnect = async () => {
try {
const status = await consoleWalletPixelplex.connect({
name: 'My Awesome dApp',
icon: 'https://example.com/icon.png', // Optional: absolute URL to your dApp icon
});
if (status === 'connected') {
console.log('Successfully connected to Console Wallet');
// Proceed with your dApp logic
}
} catch (error) {
console.error('Connection rejected or failed:', error);
}
};
The SDK supports three connection targets:
local — browser extension onlyremote — mobile app session only (QR / deep link)combined — chooses the best available connector (extension or mobile)Use target: 'remote' when you want to force the mobile/QR flow:
const connectWithMobile = async () => {
try {
const status = await consoleWalletPixelplex.connect({
name: 'My Awesome dApp',
icon: 'https://example.com/icon.png',
target: 'remote',
});
if (status.isConnected) {
console.log('Connected through mobile flow');
}
} catch (error) {
console.error('Mobile connect failed:', error);
}
};
How the remote flow behaves:
Notes for integration:
document.body.console-wallet-connect-placeholder.target: 'local'.target: 'combined'.Once connected, retrieve the active account:
const getAccount = async () => {
try {
const account = await consoleWalletPixelplex.getPrimaryAccount();
if (account) {
console.log('Active account:', account.partyId);
console.log('Network:', account.networkId);
console.log('Public key:', account.publicKey);
}
} catch (error) {
console.error('Error getting account:', error);
}
};
The SDK exposes several high-level request methods that communicate with the Console Wallet Extension through secure message passing. Each request is automatically tagged with a unique request ID to ensure reliable response matching.
| Method | Description | Request Payload | Response |
|---|---|---|---|
connect(data) | Prompts the user to connect their Console Wallet to the DApp (target: local / remote / combined). | ConnectRequest | ConnectResponse |
status() | Returns current connection status for the dApp origin. | — | StatusEvent |
disconnect() | Disconnects the DApp from the wallet. | — | DisconnectResponse |
checkExtensionAvailability() | Checks whether the wallet browser extension is installed. | — | AvailabilityResponse |
isConnected() | Checks if the network is available. | — | ConnectResponse |
getAccounts() | Retrieves all account(s) basic data. | — | GetAccountsResponse |
getPrimaryAccount() | Returns the currently selected account in the Wallet. | — | GetAccountResponse |
getActiveNetwork() | Returns the currently selected network metadata. | — | Network |
getWalletVersion() | Fetches wallet version. | — | WalletVersion |
ledgerApi(request) | Raw data request to ledger API. | LedgerApiRequest | LedgerApiResponse |
getContracts(request) | Fetch active contracts filtered by party and template IDs. | ContractsRequest | ContractsResponse |
| Method | Description | Request Payload | Response |
|---|---|---|---|
signMessage(message) | Requests the user to sign a message (hex/base64). | SignMessageRequest | SignedMessageResponse |
submitCommands(data) | Signs and broadcasts a transaction to send Canton Coin. | SignSendRequest | SignSendResponse |
signBatch(data) | Signs and broadcasts a batch of transactions. | SignBatchRequest | SignBatchResponse |
submitInstructionChoice(data) | Request user to interact with pending Transfer Instruction. | SignInstructionChoiceRequest | SignInstructionChoiceResponse |
| Method | Description | Request Payload | Response |
|---|---|---|---|
getBalance() | Check party balance; includes current Canton Coin price. | GetBalanceRequest | GetBalanceResponse |
getCoinsBalance() | Check balances and prices for supported coins. | GetBalanceRequest | GetCoinsResponse |
getTokenTransfers() | Check party token transfers with pagination (indexer). | TokenTransfersRequest | TokenTransfersResponse |
getTransfer() | Check party token transfer details (indexer). | TransferRequest | TransferResponse |
getOffers() | Check party offers with pagination (indexer). | OffersRequest | OffersResponse |
getNodeTransfers() | Check standard coin transfers with pagination (wallet node). | CoinTransfersFromNodeRequest | CoinTransfersFromNodeResponse |
getNodeTransfer() | Check single transfer details (wallet node). | TransferFromNodeRequest | TransferFromNodeResponse |
getNodeOffers() | Check pending transactions/offers with pagination (wallet node). | OffersFromNodeRequest | OffersFromNodeResponse |
Request the user to sign an arbitrary message:
const signMessage = async () => {
try {
// Sign a hex-encoded message
const signature = await consoleWalletPixelplex.signMessage({
message: { hex: '0x48656c6c6f20576f726c64' }, // "Hello World" in hex
metaData: {
purpose: 'authentication',
timestamp: new Date().toISOString(),
},
});
console.log('Signature:', signature);
} catch (error) {
console.error('Signing failed:', error);
}
};
// Or sign a base64-encoded message
const signBase64Message = async () => {
try {
const signature = await consoleWalletPixelplex.signMessage({
message: { base64: 'SGVsbG8gV29ybGQ=' }, // "Hello World" in base64
});
console.log('Signature:', signature);
} catch (error) {
console.error('Signing failed:', error);
}
};
Submit a transaction to send Canton Coin:
const sendTransaction = async () => {
try {
// Get the active account first
const activeAccount = await consoleWalletPixelplex.getPrimaryAccount();
if (!activeAccount) {
throw new Error('No active account found');
}
// Submit the transaction
const result = await consoleWalletPixelplex.submitCommands({
from: activeAccount.partyId,
to: 'receiver::fingerprint',
token: 'CC', // or 'CBTC', 'USDCx'
amount: '10.5',
expireDate: new Date(Date.now() + 24 * 60 * 60 * 1000).toISOString(), // 24 hours from now
memo: 'Payment for services', // Optional memo
waitForFinalization: 5000, // Optional: wait up to 5 seconds for finalization (between 2000-10000 ms)
});
if (result?.status) {
console.log('Transaction submitted successfully');
if (result.signature) {
console.log('Transaction signature:', result.signature);
}
if (result.confirmationData) {
console.log('Confirmation data:', result.confirmationData);
}
} else {
console.error('Transaction failed');
}
} catch (error) {
console.error('Error sending transaction:', error);
}
};
Sign and send multiple transactions in a batch:
const signBatchTransactions = async () => {
try {
const activeAccount = await consoleWalletPixelplex.getPrimaryAccount();
if (!activeAccount) {
throw new Error('No active account found');
}
const result = await consoleWalletPixelplex.signBatch({
batchType: 'SEND',
requests: [
{
from: activeAccount.partyId,
to: 'receiver1::fingerprint',
token: 'CC',
amount: '5.0',
expireDate: new Date(Date.now() + 24 * 60 * 60 * 1000).toISOString(),
type: 'OFFER',
},
{
from: activeAccount.partyId,
to: 'receiver2::fingerprint',
token: 'CC',
amount: '3.5',
expireDate: new Date(Date.now() + 24 * 60 * 60 * 1000).toISOString(),
type: 'DIRECT_TRANSFER',
},
],
});
if (result?.status) {
console.log('Batch signed successfully');
if (result.signatures) {
console.log('Signatures:', result.signatures);
} else if (result.signature) {
console.log('Signature:', result.signature);
}
}
} catch (error) {
console.error('Error signing batch:', error);
}
};
Check the balance for a party (CC balance only):
const checkBalance = async () => {
try {
const activeAccount = await consoleWalletPixelplex.getPrimaryAccount();
const activeNetwork = await consoleWalletPixelplex.getActiveNetwork();
if (!activeAccount || !activeNetwork) {
throw new Error('No active account or network found');
}
const balance = await consoleWalletPixelplex.getBalance({
party: activeAccount.partyId,
network: activeNetwork.id,
});
console.log('CC utxos:', balance.tokens);
console.log('Is split balance:', balance.isSplitedBalance);
console.log('1 CC price:', balance.price);
// Access individual token balances
balance.tokens.forEach((token) => {
console.log(`${token.symbol}: ${token.balance} (USD: ${token.balanceUsd || 'N/A'})`);
});
} catch (error) {
console.error('Error getting balance:', error);
}
};
Get detailed balance information with prices for CC and all CIP-56 tokens:
const getCoinsBalance = async () => {
try {
const activeAccount = await consoleWalletPixelplex.getPrimaryAccount();
const activeNetwork = await consoleWalletPixelplex.getActiveNetwork();
if (!activeAccount || !activeNetwork) {
throw new Error('No active account or network found');
}
const coinsBalance = await consoleWalletPixelplex.getCoinsBalance({
party: activeAccount.partyId,
network: activeNetwork.id,
});
console.log('Tokens:', coinsBalance.tokens);
console.log('Prices:', coinsBalance.prices);
} catch (error) {
console.error('Error getting coins balance:', error);
}
};
Query transaction history with pagination:
const getTransactionHistory = async () => {
try {
const activeAccount = await consoleWalletPixelplex.getPrimaryAccount();
if (!activeAccount) {
throw new Error('No active account found');
}
// Get token transfers from indexer (only CC)
const transfers = await consoleWalletPixelplex.getTokenTransfers({
party: activeAccount.partyId,
limit: 10,
cursor: '0',
});
console.log('Transfers:', transfers.data);
// Get token transfers directly from node (separated by token) *Preferred
const cip56Transfers = await consoleWalletPixelplex.getNodeTransfers({
query: { partyId: partyId, limit: 10, coin: 'CBTC', offset: 0 },
network,
});
console.log('Transfers', cip56Transfers?.items);
// Get offers from indexer (Only CC)
const offers = await consoleWalletPixelplex.getOffers({
party: activeAccount.partyId,
limit: 10,
cursor: '0',
});
console.log('Offers:', offers.data);
// Get offers from node (All tokens) *Preferred
const nodeOffers = await consoleWalletPixelplex.getNodeOffers({
query: { party_id: partyId, limit: 10, cursor: '0' },
network,
});
console.log('nodeOffers', nodeOffers?.items);
} catch (error) {
console.error('Error getting transaction history:', error);
}
};
The SDK provides subscription-style helpers to watch for changes from the Console Wallet. These functions register a callback and invoke it whenever the corresponding state changes.
| Method | Description | Callback Payload |
|---|---|---|
onAccountsChanged(onChange) | Subscribes to active account changes | GetAccountResponse |
onConnectionStatusChanged(onChange) | Subscribes to wallet connection status changes | ConnectResponse |
onTxStatusChanged(onChange) | Subscribes to transaction status lifecycle updates | TxChangedEvent |
// Subscribe to account changes
consoleWalletPixelplex.onAccountsChanged((account) => {
if (account) {
console.log('Active account changed:', account.partyId);
// Update your UI with the new account
} else {
console.log('No active account');
}
});
// Subscribe to connection status changes
consoleWalletPixelplex.onConnectionStatusChanged((status) => {
console.log('Connection status changed:', status);
if (status === 'connected') {
// User connected, enable features
} else {
// User disconnected, disable features
}
});
// Subscribe to transaction status updates
consoleWalletPixelplex.onTxStatusChanged((event) => {
console.log('Transaction status update:', event);
// Handle transaction lifecycle events (pending, confirmed, failed, etc.)
});
All request helpers return Promises and can reject with a ConsoleWalletError when something goes wrong.
The ConsoleWalletError is fully compatible with EIP-1474 error codes, ensuring a standardized error handling experience similar to other blockchain ecosystems.
type ConsoleWalletError = Error & {
name: string;
message: string;
code: number; // EIP-1474 error code
data?: unknown;
};
| Code | Name | Description |
|---|---|---|
| 4001 | UserRejected | User rejected the request. |
| 4100 | Unauthorized | Unauthorized. |
| 4200 | UnsupportedMethod | Unsupported method. |
| 4900 | Disconnected | Disconnected from the wallet. |
| 4901 | ChainDisconnected | Disconnected from the chain. |
| -32700 | ParseError | Parse error. |
| -32600 | InvalidRequest | Invalid request. |
| -32601 | MethodNotFound | Method not found. |
| -32602 | InvalidParams | Invalid params. |
| -32603 | InternalError | Internal error. |
| -32000 | InvalidInput | Invalid input. |
| -32001 | ResourceNotFound | Resource not found. |
| -32002 | ResourceUnavailable | Resource unavailable. |
| -32003 | TransactionRejected | Transaction rejected. |
| -32004 | MethodNotSupported | Method not supported. |
| -32005 | LimitExceeded | Limit exceeded. |
ConsoleWalletError payload.ConsoleWalletError rejections where applicable (for example, account and network queries).checkAvailability(), internally handle timeouts and resolve with a best-effort status instead of rejecting.You should always wrap calls in try/catch and handle both expected and unexpected errors:
try {
const accounts = await consoleWalletPixelplex.getAccounts();
// happy path
} catch (error) {
const err = error as ConsoleWalletError;
if (err.code === 4001) {
console.log('User rejected the request');
} else {
console.error('An error occurred:', err.message);
}
}
When submitting transactions, you can include optional metadata:
const transactionWithMetadata = await consoleWalletPixelplex.submitCommands({
from: activeAccount.partyId,
to: 'receiver::fingerprint',
token: 'CC',
amount: '10.0',
expireDate: new Date(Date.now() + 24 * 60 * 60 * 1000).toISOString(),
memo: 'Payment for services', // Optional memo stored as transfer metadata
});
The memo field allows you to attach additional information to the transaction, which is stored as transfer metadata and can be retrieved when querying transaction history.
All transactions on Canton are free. There are no transaction fees, traffic consumption costs, or native token costs for transaction processing.
The Console DApp SDK provides a communication layer between your Web3 application and the Console Wallet Extension using the browser's native window.postMessage API.
It handles all low-level messaging logic automatically, so you can focus on building your DApp — not managing communication details.
When your DApp sends a request (e.g., connect(), signMessage(), or submitCommands()), the SDK transmits a structured message to the Console Wallet Extension via window.postMessage.
Each outgoing request is assigned a unique request ID, which is included in both the request and the extension's response.
The SDK listens for incoming responses from the extension, matches them to their original request using the ID, and automatically resolves the corresponding Promise in your application.
This approach ensures reliable, asynchronous communication between the DApp and the extension — preventing race conditions, mismatched responses, or orphaned message handlers.
For detailed API reference, see the TypeScript type definitions in src/types/. All methods are fully typed and include JSDoc comments.
The SDK exports all relevant types:
import type {
ConnectRequest,
ConnectResponse,
GetAccountResponse,
SignMessageRequest,
SignedMessageResponse,
SignSendRequest,
SignSendResponse,
SignBatchRequest,
SignBatchResponse,
GetBalanceRequest,
GetBalanceResponse,
// ... and more
} from '@console-wallet/dapp-sdk';
The SDK provides utility functions for correct data format conversion required for working with the network and extension:
import { utils } from '@console-wallet/dapp-sdk';
// Parsers for format conversion
utils.toHex(u8: Uint8Array): string
utils.toBase64(u8: Uint8Array): string
utils.hexToBytes(hex: string): Uint8Array
utils.hexToBase64(hex: string): string
utils.base64ToBytes(base64: string): Uint8Array
utils.base64toHex(base64: string): string
// Checks
utils.equalBytes(a: Uint8Array, b: Uint8Array): boolean
These utilities are essential for converting data between different formats (hex, base64, bytes) that the network and extension expect.
See CHANGELOG.md for a list of changes and version history.
FAQs
Console DApp SDK allows dApps to connect to a Console Wallet account. The Console wallet is a browser extension. All interaction happens inside the dApp. For signing, users will be prompted to sign in their Console Wallet browser extension.
We found that @console-wallet/dapp-sdk demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Research
A supply chain attack on Axios introduced a malicious dependency, plain-crypto-js@4.2.1, published minutes earlier and absent from the project’s GitHub releases.

Research
Malicious versions of the Telnyx Python SDK on PyPI delivered credential-stealing malware via a multi-stage supply chain attack.

Security News
TeamPCP is partnering with ransomware group Vect to turn open source supply chain attacks on tools like Trivy and LiteLLM into large-scale ransomware operations.