The Adena Wallet SDK is a TypeScript-based library designed to interact with the Adena Wallet and TM2 Wallet.
This SDK provides various functionalities, including transaction signing, arbitrary message signing (ADR-036), wallet connection, network switching, etc.
(NPM Published at https://www.npmjs.com/package/@adena-wallet/sdk)
Install the SDK via npm:
npm install @adena-wallet/sdk
Or via yarn:
yarn add @adena-wallet/sdk
Here is a basic example of how to use the SDK:
import { AdenaSDK, AdenaWalletProvider } from '@adena-wallet/sdk';
// `WalletProvider` is the interface; pass a concrete provider —
// `AdenaWalletProvider` for the extension, `GnoWalletProvider` /
// `GnoSocialWalletProvider` for a locally held key.
const walletProvider = new AdenaWalletProvider();
const adenaSDK = new AdenaSDK(walletProvider);
// Connect to the wallet
adenaSDK.connectWallet().then(() => {
console.log('Wallet connected');
});
// Retrieves account information
adenaSDK.getAccount().then((account) => {
console.log('Account:', account);
});
Connects to a wallet via the SDK.
Example:
adenaSDK.connectWallet().then(() => {
console.log('Wallet connected');
});
Disconnects from a wallet via the SDK.
Example:
adenaSDK.disconnectWallet();
Retrieves the current connection state of a wallet.
Example:
const state = adenaSDK.getConnectionState();
console.log('Connection state:', state);
Sets up a listener for connection state changes.
Example:
adenaSDK.onConnectionChange({
callback: (event) => {
console.log('Connection event:', event);
},
});
Removes a previously set connection state change listener.
Example:
const listener = (event) => {
console.log('Connection event:', event);
};
adenaSDK.onConnectionChange({ callback: listener });
// Later, remove the listener
adenaSDK.offConnectionChange({ callback: listener });
Checks if a wallet is currently connected.
Example:
adenaSDK.isConnected().then((response) => {
console.log('Is connected:', response.status);
});
Retrieves account information from the connected wallet.
Example:
adenaSDK.getAccount().then((account) => {
console.log('Account:', account);
});
Retrieves network information from the connected wallet.
Example:
adenaSDK.getNetwork().then((network) => {
console.log('Network:', network);
});
Switches the wallet to a different network. With GnoWalletProvider, an unreachable RPC returns
NETWORK_TIMEOUT and leaves the previously connected network active.
Example:
adenaSDK.switchNetwork({ chainId: 'new-chain-id' }).then((response) => {
console.log('Network switch:', response.status, response.type);
});
Adds a new network to the wallet.
Example:
adenaSDK.addNetwork({
chainId: 'new-chain-id',
chainName: 'New Network',
rpcUrl: 'New Chain RPC URL',
}).then(() => {
console.log('Network added');
});
Signs a transaction with the connected wallet.
Example:
const tx = {
/** specific message types */
messages: Any[];
/** transaction costs (fee) */
fee?: TxFee;
/** the signatures for the transaction */
signatures: TxSignature[];
/** memo attached to the transaction */
memo: string;
};
adenaSDK.signTransaction({ tx }).then((response) => {
console.log('Signed transaction:', response.encodedTransaction);
});
Broadcasts a signed transaction to the network.
Example:
const tx = {
/** specific message types */
messages: Any[];
/** transaction costs (fee) */
fee?: TxFee;
/** the signatures for the transaction */
signatures: TxSignature[];
/** memo attached to the transaction */
memo: string;
};
adenaSDK.broadcastTransaction({ tx }).then((response) => {
console.log('Broadcast response:', response);
});
Signs arbitrary off-chain data with the connected account, following ADR-036. Use it to prove that a user controls an address (e.g. sign-in, ownership checks) — nothing is broadcast and no funds move.
Parameters:
- signer: Address expected to sign. The wallet answers
ACCOUNT_MISMATCHif it is not the current account - data: UTF-8 text shown to the user and signed as-is (non-empty, at most 4096 bytes)
The signed document is a single sign/MsgSignData message with zero account number, sequence and fee. Unlike plain ADR-036, chain_id carries the connected network instead of '', so a proof issued on one network cannot be replayed on another.
Note: Requires an Adena extension version that exposes
SignArbitrary; older versions answerUNSUPPORTED_TYPE.
Example:
const account = await adenaSDK.getAccount();
const nonce = await fetchNonceFromServer();
const response = await adenaSDK.signArbitrary({
signer: account.data.address,
data: `Sign in to example.com\nNonce: ${nonce}`,
});
// response.data structure:
// {
// signed: {
// chain_id: "gnoland1",
// account_number: "0",
// sequence: "0",
// fee: { amount: [], gas: "0" },
// msgs: [{ type: "sign/MsgSignData", value: { signer: "g1...", data: "<base64 of data>" } }],
// memo: ""
// },
// signature: {
// pubKey: { typeUrl: "/tm.PubKeySecp256k1", value: "<base64 compressed key>" },
// signature: "<base64 r||s>"
// }
// }Sets up a listener for account changes.
Example:
adenaSDK.onChangeAccount({
callback: (account) => {
console.log('Account changed:', account);
},
});
Sets up a listener for network changes.
Example:
adenaSDK.onChangeNetwork({
callback: (network) => {
console.log('Network changed:', network);
},
});
Creates a new multisig account with specified signers and threshold.
Parameters:
- signers: Array of signer addresses
- threshold: Minimum number of signatures required
- noSort (Optional): Whether to skip sorting signers
Example:
const multisigAccountParams = {
signers: [
"g1signer1address...",
"g1signer2address...",
"g1signer3address...",
],
threshold: 2,
noSort: true,
};
await adenaSDK.createMultisigAccount(multisigAccountParams);Creates a multisig transaction that can be signed by multiple signers. Returns the unsigned amino transaction that should be shared with all signers.
Note: This transaction must be created from a multisig account. Make sure you have switched to the multisig account in Adena wallet before calling this method.
Parameters:
- messages: Array of transaction messages
- fee:
{ gasFee, gasWanted }, both as strings (gasFeeis a<amount><denom>coin) - memo (Optional): Transaction memo
- networkInfo (Optional):
{ chainId, rpcUrl }to pin the transaction to a specific network
Example:
const multisigTxParams = {
messages: [
{
type: "/vm.m_call",
value: {
caller: "g1multisigaddress...",
send: "",
max_deposit: "",
pkg_path: "gno.land/r/gnoland/wugnot",
func: "Approve",
args: ["g1recipient...", "1000"],
},
},
],
fee: {
gasFee: "6113ugnot",
gasWanted: "6112955",
},
memo: "",
};
const response = await adenaSDK.createMultisigTransaction(multisigTxParams);
// response.data structure:
// {
// tx: {
// msg: [...],
// fee: { gas_wanted: "6112955", gas_fee: "6113ugnot" },
// signatures: null,
// memo: ""
// }
// }The signing methods take a multisig document — the transaction plus the account context it
is signed against. Adena no longer returns that context from createMultisigTransaction, so
assemble it from getAccount and getNetwork:
const account = await adenaSDK.getAccount();
const network = await adenaSDK.getNetwork();
const multisigDocument = {
tx: response.data.tx,
chainId: network.data.chainId,
accountNumber: account.data.accountNumber,
sequence: account.data.sequence,
};Signs a multisig transaction with the current account. This method adds your signature to an existing multisig transaction document.
Parameters:
- multisigDocument:
{ tx, chainId, accountNumber, sequence } - multisigSignatures (Optional): only for 2nd signer onwards, from previous signer's result
Example:
// Signer 1: Received multisigDocument from createMultisigTransaction
const signer1Response = await adenaSDK.signMultisigTransaction({ multisigDocument });
// Signer 2: Received result from Signer 1
const { multisigDocument: doc2, multisigSignatures: sigs2 } = signer1Response.data.result;
const signer2Response = await adenaSDK.signMultisigTransaction({
multisigDocument: doc2,
multisigSignatures: sigs2,
});
// Signer 3: Received result from Signer 2
const { multisigDocument: doc3, multisigSignatures: sigs3 } = signer2Response.data.result;
const signer3Response = await adenaSDK.signMultisigTransaction({
multisigDocument: doc3,
multisigSignatures: sigs3,
});Each collected signature is an amino secp256k1 signature:
// {
// pub_key: { "@type": "/tm.PubKeySecp256k1", value: "A1b2..." },
// signature: "c3D4..."
// }Broadcasts a multisig transaction once enough signatures have been collected to meet the threshold requirement.
Important: This method must be called from the same multisig account that created the transaction. Switch to the multisig account in Adena wallet before broadcasting.
Parameters:
- multisigDocument: from the final signer's result
- multisigSignatures: from the final signer's result
Example:
// Received result from the final signer (when threshold is met)
const { multisigDocument, multisigSignatures } = finalSignerResponse.data.result;
await adenaSDK.broadcastMultisigTransaction({ multisigDocument, multisigSignatures });Generate transaction data.
makeMsgSendMessage: Generates aMsgSendof bank transaction message.makeAddPackageMessage: Generate aAddPackageof vm transaction message.makeMsgCallMessage: Generate aMsgCallof vm transaction message.makeMsgRunMessage: Generates aMsgRunof vm transaction message.makeMsgEnablePackageMessage: Generates aMsgEnablePackageof vm transaction message.makeMsgRejectPackageMessage: Generates aMsgRejectPackageof vm transaction message.makeMsgCreateSessionMessage: Generates aMsgCreateSessionof auth transaction message.makeMsgRevokeSessionMessage: Generates aMsgRevokeSessionof auth transaction message.makeMsgRevokeAllSessionsMessage: Generates aMsgRevokeAllSessionsof auth transaction message.
Example:
const tx = TransactionBuilder.create()
.messages(
makeMsgSendMessage({
from_address: account.data?.address || '',
to_address: account.data?.address || '',
amount: '1ugnot',
}),
// You can add multiple messages
)
.memo('memo')
.build();
Verify a signArbitrary proof anywhere — in the browser or on a server — without a wallet or network access.
verifyArbitrarySignature(proof, expected?): Resolvestrueonly when the public key derives the signer address, the document is the canonical ADR-036 form, and the signature is a valid low-S Secp256k1 signature over it. Any field inexpected(signer,chainId,data) must match too. Never throws; malformed input resolves tofalse.parseArbitrarySignDoc(doc): Returns{ chainId, signer, data }withdatadecoded, ornullif the document is not a singlesign/MsgSignDatamessage.makeArbitrarySignDoc(chainId, signer, data): Builds the document that is signed.getArbitrarySignBytes(doc): Serializes a document into the signed bytes (sorted JSON, byte-identical to@cosmjs/amino'sserializeSignDoc).
Always pin
signer,chainIdand the exactdata(including a single-use nonce) when verifying, otherwise a valid proof can be replayed. PassingchainId: ''verifies standard Cosmos ADR-036 signatures produced by other wallets.
Example:
import { verifyArbitrarySignature } from '@adena-wallet/sdk';
const isValid = await verifyArbitrarySignature(response.data, {
signer: account.data.address,
chainId: 'gnoland1',
data: `Sign in to example.com\nNonce: ${nonce}`,
});The Node.js version is 24.13.0.
We recommend using nvm.
# If you don't have that version installed,
# $ nvm install
$ nvm useThis project is licensed under the MIT License.