Skip to content

Commit 0cd2683

Browse files
chore(examples): ada spilt UTXOs example script
ada split UXTOs example script using ada SDK Ticket: WIN-8061
1 parent 8c52bfd commit 0cd2683

File tree

1 file changed

+133
-0
lines changed

1 file changed

+133
-0
lines changed

examples/ts/ada/adaSplitUtxos.ts

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
/**
2+
* ADA UTXO Split Script
3+
* Splits UTXOs into 2 ADA outputs (last output may be 1-3 ADA)
4+
* Example split transaction: https://preprod.cardanoscan.io/transaction/f7094b57e3729c6fc2908f63be1d8e6ab91af587ae8e8112faea5c74f2e57155?tab=utxo
5+
*/
6+
7+
import { coins } from '@bitgo/statics';
8+
import { TransactionBuilderFactory, Transaction } from '@bitgo/sdk-coin-ada';
9+
import * as CardanoWasm from '@emurgo/cardano-serialization-lib-nodejs';
10+
import axios from 'axios';
11+
12+
const CONFIG = {
13+
privateKey: '',
14+
address: '',
15+
denominationAda: 2,
16+
};
17+
18+
const KOIOS_API = 'https://preprod.koios.rest/api/v1';
19+
const ADA = 1_000_000;
20+
21+
async function splitUtxos() {
22+
// Step 1: Select unspents
23+
const { utxos, total } = await selectUnspents();
24+
if (!utxos.length) throw new Error('No UTXOs found');
25+
26+
const numOutputs = Math.floor(Number(total) / ADA / CONFIG.denominationAda);
27+
if (numOutputs < 2) throw new Error('Insufficient funds for split');
28+
29+
// Step 2: Generate transaction
30+
const unsignedTx = await generateTransaction(utxos, total);
31+
32+
// Step 3: Sign transaction
33+
const signedTx = signTransaction(unsignedTx);
34+
35+
// Step 4: Submit transaction
36+
await submitTransaction(signedTx);
37+
}
38+
39+
splitUtxos()
40+
.then(() => process.exit(0))
41+
.catch((e: Error) => {
42+
console.error('Error:', e.message);
43+
process.exit(1);
44+
});
45+
46+
interface UTXO {
47+
tx_hash: string;
48+
tx_index: number;
49+
value: string;
50+
}
51+
52+
/**
53+
* Step 1: Select unspents
54+
* Fetches all UTXOs for the address and calculates total balance
55+
*/
56+
async function selectUnspents(): Promise<{ utxos: UTXO[]; total: bigint }> {
57+
const response = await axios.post(
58+
`${KOIOS_API}/address_info`,
59+
{ _addresses: [CONFIG.address] },
60+
{ headers: { 'Content-Type': 'application/json' }, timeout: 30000 }
61+
);
62+
63+
const utxos: UTXO[] = response.data?.[0]?.utxo_set || [];
64+
const total = utxos.reduce((sum, u) => sum + BigInt(u.value), BigInt(0));
65+
66+
console.log(`Step 1: Found ${utxos.length} UTXOs, total ${Number(total) / ADA} ADA`);
67+
return { utxos, total };
68+
}
69+
70+
/**
71+
* Step 2: Generate transaction with outputs
72+
* Creates (N-1) outputs of exact denomination, last output handled by changeAddress
73+
*/
74+
async function generateTransaction(utxos: UTXO[], total: bigint): Promise<Transaction> {
75+
const denom = BigInt(CONFIG.denominationAda * ADA);
76+
const numOutputs = Math.floor(Number(total) / ADA / CONFIG.denominationAda);
77+
78+
const factory = new TransactionBuilderFactory(coins.get('tada'));
79+
const txBuilder = factory.getTransferBuilder();
80+
81+
// Add inputs
82+
utxos.forEach((u) => txBuilder.input({ transaction_id: u.tx_hash, transaction_index: u.tx_index }));
83+
84+
// Add (N-1) outputs of exact denomination
85+
for (let i = 0; i < numOutputs - 1; i++) {
86+
txBuilder.output({ address: CONFIG.address, amount: denom.toString() });
87+
}
88+
89+
// Last output handled by changeAddress (will be ~1-3 ADA)
90+
txBuilder.changeAddress(CONFIG.address, total.toString());
91+
92+
const tip = await axios.get(`${KOIOS_API}/tip`, { timeout: 30000 });
93+
txBuilder.ttl(tip.data[0].abs_slot + 7200);
94+
95+
const tx = (await txBuilder.build()) as Transaction;
96+
console.log(`Step 2: Built tx with ${numOutputs} outputs, fee ${Number(tx.getFee) / ADA} ADA`);
97+
return tx;
98+
}
99+
100+
/**
101+
* Step 3: Sign transaction
102+
* Signs the transaction with the private key
103+
*/
104+
function signTransaction(tx: Transaction): Transaction {
105+
const priv = CardanoWasm.PrivateKey.from_bech32(CONFIG.privateKey);
106+
const hash = CardanoWasm.hash_transaction(tx.transaction.body());
107+
108+
const witnessSet = CardanoWasm.TransactionWitnessSet.new();
109+
const vkeyWitnesses = CardanoWasm.Vkeywitnesses.new();
110+
vkeyWitnesses.add(CardanoWasm.make_vkey_witness(hash, priv));
111+
witnessSet.set_vkeys(vkeyWitnesses);
112+
113+
tx.transaction = CardanoWasm.Transaction.new(tx.transaction.body(), witnessSet, tx.transaction.auxiliary_data());
114+
115+
console.log(`Step 3: Signed tx ${tx.toJson().id}`);
116+
return tx;
117+
}
118+
119+
/**
120+
* Step 4: Submit transaction
121+
* Broadcasts the signed transaction to the network
122+
*/
123+
async function submitTransaction(tx: Transaction): Promise<void> {
124+
const signedTxHex = tx.toBroadcastFormat();
125+
const bytes = Uint8Array.from(Buffer.from(signedTxHex, 'hex'));
126+
127+
await axios.post(`${KOIOS_API}/submittx`, bytes, {
128+
headers: { 'Content-Type': 'application/cbor' },
129+
timeout: 30000,
130+
});
131+
132+
console.log(`Step 4: https://preprod.cardanoscan.io/transaction/${tx.toJson().id}`);
133+
}

0 commit comments

Comments
 (0)