cardano-wallet-js (powered by TANGO stake pool)
cardano-wallet-js
is a javascript/typescript SDK for Cardano with a several functionalities. You can use it as a client for the official cardano-wallet and also to create Native Tokens and NFTs.
Table of Contents
Introduction
The official cardano-wallet by IOHK exposes a REST api/CLI interface which allows
clients to perform common tasks on the cardano-blockchain, such as:
- creating or restoring a wallet
- submitting a transaction with or without metadata
- checking on the status of the node
- listing transactions
- listing wallets
Our project aims to provide an easy to use Javascript SDK for programmers, instead of
exposing the raw REST structure to you.
Requirements
Before start using the library you will need a cardano-wallet
server running. If you have docker available you can just
download the docker-composer.yml
they provide and start it using docker-compose
:
wget https://raw.githubusercontent.com/input-output-hk/cardano-wallet/master/docker-compose.yml
NETWORK=testnet docker-compose up
NOTE: You can find more information about different options to start the cardano-wallet server here
Installation
Using npm:
npm i cardano-wallet-js
Usage
To begin, start with a WalletServer
. It allows you to connect to some remote cardano-wallet
service.
Connecting to a cardano-wallet service
const { WalletServer } = require('cardano-wallet-js');
let walletServer = WalletServer.init('http://{your-server-host}:{port}/v2');
Blockchain Information
First you can try is getting some blockchain information like: (network parameters, information and clock)
Get network information
let information = await walletServer.getNetworkInformation();
console.log(information);
This will print out something like this:
{
"network_tip": {
"time": "2021-04-12T21:59:25Z",
"epoch_number": 125,
"absolute_slot_number": 23895549,
"slot_number": 265149
},
"node_era": "mary",
"node_tip": {
"height": {
"quantity": 0,
"unit": "block"
},
"time": "2019-07-24T20:20:16Z",
"epoch_number": 0,
"absolute_slot_number": 0,
"slot_number": 0
},
"sync_progress": {
"status": "syncing",
"progress": {
"quantity": 0,
"unit": "percent"
}
},
"next_epoch": {
"epoch_start_time": "2021-04-14T20:20:16Z",
"epoch_number": 126
}
}
Get network parameters
let parameters = await walletServer.getNetworkParameters();
console.log(parameters);
This will print out something like this:
{
"slot_length": {
"quantity": 1,
"unit": "second"
},
"decentralization_level": {
"quantity": 100,
"unit": "percent"
},
"genesis_block_hash": "96fceff972c2c06bd3bb5243c39215333be6d56aaf4823073dca31afe5038471",
"blockchain_start_time": "2019-07-24T20:20:16Z",
"desired_pool_number": 500,
"epoch_length": {
"quantity": 432000,
"unit": "slot"
},
"eras": {
"shelley": {
"epoch_start_time": "2020-07-28T20:20:16Z",
"epoch_number": 74
},
"mary": {
"epoch_start_time": "2021-02-03T20:20:16Z",
"epoch_number": 112
},
"byron": {
"epoch_start_time": "2019-07-24T20:20:16Z",
"epoch_number": 0
},
"allegra": {
"epoch_start_time": "2020-12-15T20:20:16Z",
"epoch_number": 102
}
},
"active_slot_coefficient": {
"quantity": 5,
"unit": "percent"
},
"security_parameter": {
"quantity": 2160,
"unit": "block"
},
"minimum_utxo_value": {
"quantity": 1000000,
"unit": "lovelace"
}
}
Get network clock
let clock = await walletServer.getNetworkClock();
console.log(clock);
This will print out something like this:
{
"status": "available",
"offset": {
"quantity": 405623,
"unit": "microsecond"
}
}
Useful operations
Generate Recovery Phrases
The recovery phrase generation relies on bip39.
const { Seed } = require('cardano-wallet-js');
let recoveryPhrase = Seed.generateRecoveryPhrase();
console.log(recoveryPhrase);
Output:
> "hip dust material keen buddy fresh thank program stool ill regret honey multiply venture imitate"
IMPORTANT: The recovery phrase is the only way you can restore you wallet and you SHOULD KEEP IT SECURE AND PRIVATE. You'll get a completeley different recovery phrase each time you execute the method.
For convenience, you can convert the recovery phrase into an array using this:
let words = Seed.toMnemonicList(recoveryPhrase);
console.log(words);
Output:
> ['hip', 'dust', 'material', 'keen', 'buddy', 'fresh', 'thank', 'program', 'stool', 'ill', 'regret', 'honey', 'multiply', 'venture', 'imitate']
Create or restore a wallet
In this example we are going to create a new wallet. Have in mind that the method createOrRestoreShelleyWallet
creates a new wallet if it doesn't exist or restore an existent wallet:
const { Seed, WalletServer } = require('cardano-wallet-js');
let walletServer = WalletServer.init('http://you.server.com');
let recoveryPhrase = Seed.generateRecoveryPhrase();
let mnemonic_sentence = Seed.toMnemonicList(recoveryPhrase);
let passphrase = 'tangocrypto';
let name = 'tangocrypto-wallet';
let wallet = await walletServer.createOrRestoreShelleyWallet(name, mnemonic_sentence, passphrase);
List wallets:
let wallets = await walletServer.wallets();
Get wallet by Id:
let wallets = await walletServer.wallets();
let id = wallets[0].id;
let wallet = await walletServer.getShelleyWallet(id);
Get wallet's utxo statistics:
let statistics = await wallet.getUtxoStatistics();
Statistics will contain the UTxOs distribution across the whole wallet, in the form of a histogram similar to the one below.
│
100 ─
│
│ ┌───┐
10 ─ ┌───┐ │ │ ┌───┐
│ ┌───┐ │ │ │ │ │ │
│ │ │ │ │ │ │ ┌───┐ │ │
1 ─ ┌───┐ │ │ │ │ │ │ │ │ │ │
│ │ │ │ │ │ │ │ │ │ │ │ │
│ │ │ │ │ │ │ │ │ │ ╷ │ │ ╷ │ │ ╷ ╷ │ │
└─┘ └─│───────│─┘ └─│─┘ └─│─┘ └─│─┘ └─│───────│─┘ └────
10μ₳ 100μ₳ 1000μ₳ 0.1₳ 1₳ 10₳ 100₳
Remove wallet:
await wallet.delete();
Rename wallet:
let newName = 'new-name';
wallet = await wallet.rename(newName);
Change wallet passphrase:
let oldPassphrase = 'tangocrypto';
let newPassphrase = 'new-passphrase';
wallet = await wallet.updatePassphrase(oldPassphrase, newPassphrase);
NOTE: the wallet itself doesn't hold the passphrase, you can check it's correctly updated trying to call a method needing the passphrase e.g: sendPayment
Wallet addresses
Cardano wallets are Multi-Account Hierarchy Deterministic that follow a variation of BIP-44 described here. All the addresses are derived from a root key (is like a key factory) which you can get from the recovery phrase. Also the wallets will always have 20 "consecutive" unused address, so anytime you use one of them new address will be "discovered" to keep the rule.
let addresses = await wallet.getAddresses();
Get unused addresses:
let unusedAddresses = await wallet.getUnusedAddresses();
Get used addresses:
let usedAddresses = await wallet.getUsedAddresses();
You can create/discover next unused address:
let address = await wallet.getNextAddress();
let address = await wallet.getAddressAt(45);
Wallet balances
When you create a wallet the initial balance is 0. If you are in the mainnet you can transfer Ada to this address. If you are on the testnet you can request test tokens from the Faucet, just input one of the addresses of your wallet and request funds.
let availableBalance = wallet.getAvailableBalance();
let rewardBalance = wallet.getRewardBalance();
let totalBalance = wallet.getTotalBalance();
Wallet delegation
The wallet have information about whether already delegate on a stake pool or not
let delegation = wallet.getDelegation();
console.log(delegation);
It the wallet is not delegate to any stake pool the output should be something similar to this:
{
"next": [],
"active": {
"status": "not_delegating"
}
}
If you start delegating (see Stake pool section the action will not take effect inmediatelly but the next
property will indicate when the delegation will finally take effect.
The delegation meanwhile should look like this:
{
"next": [{
"status": "delegating",
"changes_at": {
"epoch_start_time": "2021-04-15T15:03:27Z",
"epoch_number": 10
},
"target": "pool1as50x0wtumtyqzs7tceeh5ry0syh8jnvpnuu9wlxswxuv48sw4w"
}],
"active": {
"status": "not_delegating"
}
}
NOTE: Property changes_at
will indicate the epoch at the delegation will take effect
If we ask again after/during the epoch 10, we should get the delgation in place:
await wallet.refresh();
let delegation = wallet.getDelegation();
console.log(delegation);
Output:
> {
next: [],
active: {
status: 'delegating',
target: 'pool1as50x0wtumtyqzs7tceeh5ry0syh8jnvpnuu9wlxswxuv48sw4w'
}
}
Stake pool operations with the wallet
Get stake pool ranking list by member rewards:
let stake = 1000000000;
let pools = await walletServer.getStakePools(stake);
NOTE: You'll get pool ordered by non_myopic_member_rewards
which basically means from heighest to lower expected rewards. By default the wallet server
isn't configured to fecth the pool's metadata (e.g. ticker, name, homepage) but you can specify it through the update settings functionality, see Update Settings section below.
Estimate delegation fee:
let fee = await wallet.estimateDelegationFee();
NOTE: The very first time you delegate to a pool you'll be charged an extra 2 ADA. This extra fee won't be included on the response.
Delegate to stake pool:
let passphrase = 'tangocrypto';
let pool = pools[0];
let transaction = await wallet.delegate(pool.id, passphrase);
NOTE: The transacion status initially is set to pending
, so you should keep tracking the transaction using the id
in order to make sure the final status (e.g. in_ledger
). You can learn more about the transacion's life cycle here.
For delegate to another stake pool use the same method above specifying a different stake pool.
Withdraw stake pool's rewards:
let passphrase = 'tangocrypto';
let address = (await wallet.getUsedAddresses())[0];
let rewardBalance = wallet.getRewardBalance();
let transaction = await wallet.withdraw(passphrase, [address], [rewardBalance]);
NOTE: You can send the rewards to multiple addresses splitting up the rewardBalance for each one. Also you can send it to any valid address whether it's in your wallet or not.
Stop delegating:
let transaction = await wallet.stopDelegation(passphrase);
Stake pool maintenance actions:
let maintenanceActions = await walletServer.stakePoolMaintenanceActions();
Possible values are:
- not_applicable -> we're currently not querying a SMASH server for metadata
- not_started -> the Garbage Collection hasn't started yet, try again in a short while
- restarting -> the Garbage Collection thread is currently restarting, try again in short while
- has_run -> the Garbage Collection has run successfully
NOTE: Maintenance actions will depend on whether or not the wallet server is using a Stakepool Metadata Aggregation Server (SMASH).
Manually trigger Garbage Collection:
await walletServer.triggerStakePoolGarbageCollection();
Wallet transactions
Get wallet transactions:
let transactions = await wallet.getTransactions();
let start = new Date(2021, 0, 1);
let end = new Date(Date.now());
let transactions = await wallet.getTransactions(start, end);
Get transaction details:
let transaction = await wallet.getTransaction(tx.id);
Get payment fees:
let receiverAddress = new AddressWallet('addr1q99q78gt2898zgu2dcswf2yuxj6vujcqece38rycc7wsncl5lx8y....');
let amount = 5000000;
let estimatedFees = await senderWallet.estimateFee([receiverAddress], [amount]);
Send payment transfer. Notice that you don't have to calculate the minimum fee, the SDK does that for you:
let passphrase = 'tangocrypto';
let receiverAddress = [new AddressWallet('addr1q99q78gt2898zgu2dcswf2yuxj6vujcqece38rycc7wsncl5lx8y....')];
let amounts = [5000000];
let transaction = await senderWallet.sendPayment(passphrase, receiverAddress, amounts);
NOTE: You can pass a list of address and amount. We expect both list have the same length where elemetns on each list is index related to the other.
You can think of it as sending amounts[i]
to addresses[i]
.
Send payment transfer with metadata:
Metadata can be expressed as a JSON object with some restrictions:
- All top-level keys must be integers between 0 and 264 - 1
- Each metadata value is tagged with its type.
- Strings must be at most 64 bytes when UTF-8 encoded.
- Bytestrings are hex-encoded, with a maximum length of 64 bytes.
For more information check here.
let passphrase = 'tangocrypto';
let receiverAddress = [new AddressWallet('addr1q99q78gt2898zgu2dcswf2yuxj6vujcqece38rycc7wsncl5lx8y....')];
let amounts = [5000000];
let metadata = ['abc', '2512a00e9653fe49a44a5886202e24d77eeb998f', 123];
let transaction = await senderWallet.sendPayment(passphrase, receiverAddress, amounts, metadata);
WARNING: Please note that metadata provided in a transaction will be stored on the blockchain forever. Make sure not to include any sensitive data, in particular personally identifiable information (PII).
Send a more complex metadata object:
let passphrase = 'tangocrypto';
let receiverAddress = [new AddressWallet('addr1q99q78gt2898zgu2dcswf2yuxj6vujcqece38rycc7wsncl5lx8y....')];
let amounts = [5000000];
let metadata: any = {0: 'hello', 1: Buffer.from('2512a00e9653fe49a44a5886202e24d77eeb998f', 'hex'), 4: [1, 2, {0: true}], 5: {'key': null, 'l': [3, true, {}]}, 6: undefined};
let transaction = await senderWallet.sendPayment(passphrase, receiverAddress, amounts, metadata);
NOTE: Values like boolean, null and undefined are passed as string (e.g "true", "null", "undefined").
Forget transaction:
If for some reason your transaction hang on status pending
, for a long period, you can consider to "cancel" it.
wallet.forgetTransaction(transaction.id)
Importantly: A transaction, when sent, cannot be cancelled. One can only request forgetting about it in order to try spending (concurrently) the same UTxO in another transaction. But, the transaction may still show up later in a block and therefore, appear in the wallet.
Submit external transaction
You can pass in a transaction created externally (by other tools) and submit it into the blockchain. You can use this library to create the transaction
offline as well. Here is an example:
let recovery_phrase = [...];
let config = { ..., "shelley": { ..., "protocolParams": {... "minFeeA": 44, ..., "minFeeB": 155381, ...} } }
let addresses = (await wallet.getUnusedAddresses()).slice(0, 1);
let amounts = [1000000];
let info = await walletServer.getNetworkInformation();
let ttl = info.node_tip.absolute_slot_number * 12000;
let data: any = {0: 'hello', 1: Buffer.from('2512a00e9653fe49a44a5886202e24d77eeb998f', 'hex'), 4: [1, 2, {0: true}], 5: {'key': null, 'l': [3, true, {}]}, 6: undefined};
let coinSelection = await wallet.getCoinSelection(addresses, amounts, data);
let rootKey = Seed.deriveRootKey(recovery_phrase);
let signingKeys = coinSelection.inputs.map(i => {
let privateKey = Seed.deriveKey(rootKey, i.derivation_path).to_raw_key();
return privateKey;
});
let metadata = Seed.buildTransactionMetadata(data);
let txBuild = Seed.buildTransaction(coinSelection, ttl, {metadata: metadata, config: config});
let txBody = Seed.sign(txBuild, signingKeys, metadata);
let signed = Buffer.from(txBody.to_bytes()).toString('hex');
let txId = await walletServer.submitTx(signed);
Key handling
There ara a couple of methods you can use to derive and get private/public key pairs. For more info check here.
Get root key from recovery phrase
let phrase = [...];
let rootKey = Seed.deriveRootKey(phrase);
console.log(rootKey.to_bech32());
Output:
> "xprv..."
Derive private/signing key (also known as spending key) from root key
let rootKey = Seed.deriveRootKey(phrase);
let privateKey = Seed.deriveKey(rootKey, ['1852H','1815H','0H','0','0']).to_raw_key();
console.log(privateKey.to_bech32());
Output:
> "ed25519e_sk1..."
Derive account key from root
let rootKey = Seed.deriveRootKey(phrase);
let accountKey = Seed.deriveAccountKey(rootKey, 0);
console.log(accountKey.to_bech32());
Output:
> "xprv..."
All the method mentioned above return a Bip32PrivateKey
which you can use to keep deriving and generating keys and addresses check here for more info. For example, assuming you have cardano-serialization-lib
installed,
you can get a stake address like this:
let rootKey = Seed.deriveRootKey(phrase);
let stakePrvKey = Seed.deriveKey(rootKey, ['1852H','1815H','0H','2','0']).to_raw_key();
const stakePubKey = stakePrvKey.to_public();
const rewardAddr = RewardAddress.new(
NetworkInfo.mainnet().network_id(),
StakeCredential.from_keyhash(stakePubKey.hash())
)
.to_address();
console.log(rewardAddr.to_bech32());
Output:
> "stake..."
Sign and verify a message using a private/public key pair.
let message = 'Hello World!!!';
const rootKey = Seed.deriveRootKey(phrase);
const accountKey = Seed.deriveAccountKey(rootKey);
const stakePrvKey = accountKey
.derive(CARDANO_CHIMERIC)
.derive(0);
const privateKey = stakePrvKey.to_raw_key();
const publicKey = privateKey.to_public();
const signed = Seed.signMessage(privateKey, message);
const verify_result = Seed.verifyMessage(publicKey, message, signed);
Output:
> True
Native Tokens
You can create native tokens just creating a transaction with a couple of differences, here is an example:
let addresses = [(await wallet.getAddresses())[0]];
let config = { ..., "shelley": { ..., "protocolParams": {... "minFeeA": 44, ..., "minFeeB": 155381, ...} } }
let keyPair= Seed.generateKeyPair();
let policyVKey = keyPair.publicKey;
let policySKey = keyPair.privateKey;
let keyHash = Seed.getKeyHash(policyVKey);
let script = Seed.buildSingleIssuerScript(keyHash);
let scriptHash = Seed.getScriptHash(script);
let policyId = Seed.getPolicyId(scriptHash);
let data: any = {};
let tokenData: any = {}
tokenData[policyId] = {
Tango: {
arweaveId: "arweave-id",
ipfsId: "ipfs-id",
name: "Tango",
description: "Tango crypto coin",
type: "Coin"
}
};
data[0] = tokenData;
let asset = new AssetWallet(policyId, "Tango", 1000000);
let tokens = [new TokenWallet(asset, script, [keyPair])];
let scripts = tokens.map(t => t.script);
let minAda = Seed.getMinUtxoValueWithAssets([asset], config);
let amounts = [minAda];
let info = await walletServer.getNetworkInformation();
let ttl = info.node_tip.absolute_slot_number * 12000;
let coinSelection = await wallet.getCoinSelection(addresses, amounts, data);
let rootKey = Seed.deriveRootKey(payeer.mnemonic_sentence);
let signingKeys = coinSelection.inputs.map(i => {
let privateKey = Seed.deriveKey(rootKey, i.derivation_path).to_raw_key();
return privateKey;
});
tokens.filter(t => t.scriptKeyPairs).forEach(t => signingKeys.push(...t.scriptKeyPairs.map(k => k.privateKey.to_raw_key())));
let metadata = Seed.buildTransactionMetadata(data);
coinSelection.outputs = coinSelection.outputs.map(output => {
if (output.address === addresses[0].address) {
output.assets = tokens.map(t => {
let asset: WalletsAssetsAvailable = {
policy_id: t.asset.policy_id,
asset_name: Buffer.from(t.asset.asset_name).toString('hex'),
quantity: t.asset.quantity
};
return asset;
});
}
return output;
});
let txBody = Seed.buildTransactionWithToken(coinSelection, ttl, tokens, signingKeys, {data: data, config: config});
let tx = Seed.sign(txBody, signingKeys, metadata, scripts);
let signed = Buffer.from(tx.to_bytes()).toString('hex');
let txId = await walletServer.submitTx(signed);
NOTE: You can check more scripts on test/assets.ts
, this example is the equivalent to "RequireSignature" you can create with JSON:
{
"type": "sig",
"keyHash": "e09d36c79dec9bd1b3d9e152247701cd0bb860b5ebfd1de8abb6735a"
}
Send Native Tokens
Here you have two options, either rely on cardano-wallet directly or build the tx by yourself (using cardano-serialization-lib).
Using Cardano Wallet
let passphrase = "your passphrase";
let policyId = "your policyId";
let passphrase = "your passphrase";
let policyId = "your policyId";
let config = { ..., "shelley": { ..., "protocolParams": {... "minFeeA": 44, ..., "minFeeB": 155381, ...} } }
let addresses = [new AddressWallet("addr......")];
let asset = new AssetWallet(policyId, "Tango", 100);
let assets = {};
assets[addresses[0].id] = [asset];
let minAda = Seed.getMinUtxoValueWithAssets([asset], config);
let tx = await wallet.sendPayment(passphrase, addresses, [minAda], ['send 100 Tango tokens'], assets);
Building the tx
let passphrase = "your passphrase";
let policyId = "your policyId";
let config = { ..., "shelley": { ..., "protocolParams": {... "minFeeA": 44, ..., "minFeeB": 155381, ...} } }
let addresses = [new AddressWallet("addr......")];
let asset = new AssetWallet(policyId, "Tango", 100);
let config = { ..., "shelley": { ..., "protocolParams": {... "minFeeA": 44, ..., "minFeeB": 155381, ...} } }
let assets = {};
assets[addresses[0].id] = [asset];
let minUtxo = Seed.getMinUtxoValueWithAssets([asset], config)
let data = ['send 100 Tango tokens'];
let coinSelection = await wallet.getCoinSelection(addresses, [minUtxo], data, assets);
let info = await walletServer.getNetworkInformation();
let rootKey = Seed.deriveRootKey(payeer.mnemonic_sentence);
let signingKeys = coinSelection.inputs.map(i => {
let privateKey = Seed.deriveKey(rootKey, i.derivation_path).to_raw_key();
return privateKey;
});
let metadata = Seed.buildTransactionMetadata(data);
let txBuild = Seed.buildTransaction(coinSelection, info.node_tip.absolute_slot_number * 12000, {metadata: metadata, config: config});
let txBody = Seed.sign(txBuild, signingKeys, metadata);
let signed = Buffer.from(txBody.to_bytes()).toString('hex');
let txId = await walletServer.submitTx(signed);
Build Multisig tx
In order to create a multisignature transaction (multisig tx) we must create a script that will act as a "guard" for the funds sent to the script address. Once the funds are already there, the only way to move it will be fulfilling the script logic (Multisig is just a specific script case that force a list of private keys to be present on the final tx).
Create native script
const { Seed, ScriptTypeEnum, WalletswalletIdpaymentfeesAmountUnitEnum, Config } = require('cardano-wallet-js');
const data: JsonScript = {
"type": ScriptTypeEnum.All,
"scripts":
[
{
"type": ScriptTypeEnum.Sig,
},
{
"type": ScriptTypeEnum.Sig,
}
]
};
const script = Seed.buildScript(data);
const jsonScript = Seed.scriptToJson(script);
const keys = Seed.getScriptKeys(script).map(k => k.to_bech32());
Get keyHash from private key
In the previous step, we created a native script and got the JSON representation along with the private keys involved. To check if your resulting private keys indeed match your private keys, you can do this:
const keyHashes = keys.map(k => Buffer.from(Seed.getKeyHash(Bip32PrivateKey.from_bech32(k).to_public()).to_bytes()).toString('hex'));
const jsonHashes = json.scripts.map((s:any) => s.keyHash);
expect(keyHashes).deep.equal(jsonHashes);
Generate script address
The next step is to get the script address, so we can send funds to it. Here is the example (testnet address):
const script = Seed.buildScript(jsonScript);
const address = Seed.getScriptAddress(script, 'testnet').to_bech32();
console.log(address);
Output:
> "addr_test1..."
:warning: IMPORTANT: Send some funds to this script address before continue
Send funds from script address
Now that we have the script address and the private keys tied to it, we're ready to send some funds away from this address (remember we'll need all the private keys involved, this is the multisig part). The approach is very similar to the previous one where we build a tx by ourselves. The bellow example assume we have a coin selection already, which mean we got the script address UTXOs and created the inputs, outputs and change using some method like wallet.getCoinSelection(...)
:warning: IMPORTANT: Use the exact FINAL fee you want in the coinselection. The fee is implicit on: (inputs + withdrawals) - (outputs + change + deposits) = fee.
const signingKeys = scriptKeys.map(key => Bip32PrivateKey.from_bech32(key).to_raw_key());
const script = Seed.buildScript(jsonScript);
let buildOpts = {
startSlot: 0,
config: Config.Testnet,
};
const ttl = 445331390;
const selection: CoinSelectionWallet = {
"withdrawals": [] as any[],
"inputs": [
{
"amount": {
"quantity": 5574291,
"unit": WalletswalletIdpaymentfeesAmountUnitEnum.Lovelace
},
"address": "addr_test1...",
"id": "f23e1e9c8cdcc8a3dfbd...",
"assets": [],
"index": 1
},
],
"deposits": [],
"change": [
{
"amount": {
"quantity": 4394291,
"unit": WalletswalletIdpaymentfeesAmountUnitEnum.Lovelace
},
"address": "addr_test1...",
"assets": []
}
],
"outputs": [
{
"amount": {
"quantity": 1000000,
"unit": WalletswalletIdpaymentfeesAmountUnitEnum.Lovelace
},
"address": "addr_test1...",
"assets": []
}
]
};
const scripts = [script.root];
let tx = Seed.buildTransactionMultisig(selection, ttl, [], null, [], buildOpts);
tx.addKeyWitnesses(signingKeys[0]);
tx.addScriptWitness(...scripts);
tx.addKeyWitnesses(signingKeys[1]);
const signed = tx.build();
console.log(signed)
Output:
> "84a500818258204b6a8bf9..."
Test
Stack
you'll need to install stack >= 1.9.3
you can find it here: https://docs.haskellstack.org/en/stable/README/.
You may need to install the libsodium-dev, libghc-hsopenssl-dev, gmp, sqlite and systemd development libraries for the build to succeed.
Also you will need cardano-node
and cardano-cli
binaries availables on your PATH.
The setup steps are quite simple:
clone: cardano-wallet
execute: stack install cardano-wallet:exe:local-cluster
Set a specific port export CARDANO_WALLET_PORT=7355
so the wallet always start at the same port.
run ~/.local/bin/local-cluster
Support our project
- Delegate with us:
Ticker: TANGO
Pool Id: bd5f6b254798e3ddde1a9c3609fa9d6e468d638bd9103afb02e29ae5
You can find us on Adapools.org - Donate ADA to:
addr1qxw567xyzgqchtyddeha90ugsr8uyu7h4uh8wg5su8qz7yykq2ndwcmedv72l7263vj96tl6cyn74l2mw5kp8h7fk8rqevat3x