From 33dad6c67e1b9d6ac6f5a311e99472e26a0c5aed Mon Sep 17 00:00:00 2001 From: Sergey Chystiakov Date: Mon, 1 Jun 2026 19:58:38 +0200 Subject: [PATCH 1/2] recreate utxo issue with as test case --- .../accounts/account_03_single_utxo.ts | 29 +++++++++ .../tests/staking-create-delegation.test.ts | 62 +++++++++++++++++++ 2 files changed, 91 insertions(+) create mode 100644 packages/sdk/tests/__mocks__/accounts/account_03_single_utxo.ts create mode 100644 packages/sdk/tests/staking-create-delegation.test.ts diff --git a/packages/sdk/tests/__mocks__/accounts/account_03_single_utxo.ts b/packages/sdk/tests/__mocks__/accounts/account_03_single_utxo.ts new file mode 100644 index 0000000..6b4ebd7 --- /dev/null +++ b/packages/sdk/tests/__mocks__/accounts/account_03_single_utxo.ts @@ -0,0 +1,29 @@ +export const addresses: any = { + addressesByChain: { + mintlayer: { + receiving: ['tmt1q9874wgx6enm2mzfu0yxhzleu84pp00l95l7er5z'], + change: ['tmt1qxrwc3gy2lgf4kvqwwfa388vn3cavgrqyyrgswe6'], + }, + }, +}; + +export const utxos: any = [ + { + outpoint: { + index: 0, + source_id: 'af3b5fad20f6f97eb210934e942176f7f7d0f70423590659ee0e0217053a7cab', + source_type: 'Transaction', + }, + utxo: { + destination: 'tmt1q9874wgx6enm2mzfu0yxhzleu84pp00l95l7er5z', + type: 'Transfer', + value: { + amount: { + atoms: '2000000000000', + decimal: '20', + }, + type: 'Coin', + }, + }, + }, +]; diff --git a/packages/sdk/tests/staking-create-delegation.test.ts b/packages/sdk/tests/staking-create-delegation.test.ts new file mode 100644 index 0000000..139c336 --- /dev/null +++ b/packages/sdk/tests/staking-create-delegation.test.ts @@ -0,0 +1,62 @@ +import { Client } from '../src/mintlayer-connect-sdk'; +import fetchMock from 'jest-fetch-mock'; + +import { addresses, utxos } from './__mocks__/accounts/account_03_single_utxo'; + +beforeEach(() => { + fetchMock.resetMocks(); + + (window as any).mojito = { + isExtension: true, + connect: jest.fn().mockResolvedValue(addresses), + restore: jest.fn().mockResolvedValue(addresses), + disconnect: jest.fn().mockResolvedValue(undefined), + request: jest.fn().mockResolvedValue('signed-transaction'), + }; + + fetchMock.doMock(); + + fetchMock.mockResponse(async (req) => { + const url = req.url; + + if (url.endsWith('/chain/tip')) { + return JSON.stringify({ height: 200000 }); + } + + if (url.endsWith('/batch')) { + return { + body: JSON.stringify({ + results: [utxos], + }), + }; + } + + console.warn('No mock for:', url); + return JSON.stringify({ error: 'No mock defined' }); + }); +}); + +test('create delegation uses a single coin UTXO to calculate the fee', async () => { + const client = await Client.create({ network: 'testnet', autoRestore: false }); + + await client.connect(); + + const tx: any = await client.buildDelegationCreate({ + pool_id: 'tpool1dwpe7zy0mhagnwl36ywt5q20xxvu5dwmph4z6q8sc0a3srz5h8jqr0r2yg', + destination: 'tmt1q9874wgx6enm2mzfu0yxhzleu84pp00l95l7er5z', + }); + + expect(tx.inputs).toHaveLength(1); + expect(tx.inputs[0]).toMatchObject({ + input: { + input_type: 'UTXO', + }, + }); + expect(tx.outputs).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + type: 'CreateDelegationId', + }), + ]), + ); +}); From e25ac5780f22551876251f5e63e6fa635ae2c307 Mon Sep 17 00:00:00 2001 From: Sergey Chystiakov Date: Mon, 1 Jun 2026 20:10:27 +0200 Subject: [PATCH 2/2] zero input for the first iteration is acceptable --- packages/sdk/src/mintlayer-connect-sdk.ts | 42 ++++++++----------- packages/sdk/src/transaction.ts | 40 ++++++++---------- .../tests/staking-create-delegation.test.ts | 6 +-- 3 files changed, 38 insertions(+), 50 deletions(-) diff --git a/packages/sdk/src/mintlayer-connect-sdk.ts b/packages/sdk/src/mintlayer-connect-sdk.ts index 9ba9536..772577a 100644 --- a/packages/sdk/src/mintlayer-connect-sdk.ts +++ b/packages/sdk/src/mintlayer-connect-sdk.ts @@ -2871,35 +2871,29 @@ class Client { } }).flat(); - // @ts-ignore - if (transactionJSONrepresentation.inputs[0].input.account_type === 'DelegationBalance') { - // @ts-ignore - inputAddresses.push(transactionJSONrepresentation.outputs[0].destination); + const firstInput = transactionJSONrepresentation.inputs[0]?.input as any; + + if (firstInput?.account_type === 'DelegationBalance') { + inputAddresses.push((transactionJSONrepresentation.outputs[0] as any).destination); } - // @ts-ignore - if (transactionJSONrepresentation.inputs[0].input.input_type === 'AccountCommand') { - // @ts-ignore - if (transactionJSONrepresentation.inputs[0].input.destination) { - // @ts-ignore - inputAddresses.push(transactionJSONrepresentation.inputs[0].input.destination); + + if (firstInput?.input_type === 'AccountCommand') { + if (firstInput.destination) { + inputAddresses.push(firstInput.destination); } - // @ts-ignore - if (transactionJSONrepresentation.inputs[0].input.authority) { - // @ts-ignore - inputAddresses.push(transactionJSONrepresentation.inputs[0].input.authority); + + if (firstInput.authority) { + inputAddresses.push(firstInput.authority); } } - // @ts-ignore - if (transactionJSONrepresentation.inputs[0].input.input_type === 'Account') { - // @ts-ignore - if (transactionJSONrepresentation.inputs[0].input.destination) { - // @ts-ignore - inputAddresses.push(transactionJSONrepresentation.inputs[0].input.destination); + + if (firstInput?.input_type === 'Account') { + if (firstInput.destination) { + inputAddresses.push(firstInput.destination); } - // @ts-ignore - if (transactionJSONrepresentation.inputs[0].input.authority) { - // @ts-ignore - inputAddresses.push(transactionJSONrepresentation.inputs[0].input.authority); + + if (firstInput.authority) { + inputAddresses.push(firstInput.authority); } } diff --git a/packages/sdk/src/transaction.ts b/packages/sdk/src/transaction.ts index 036c9c9..65cda1a 100644 --- a/packages/sdk/src/transaction.ts +++ b/packages/sdk/src/transaction.ts @@ -654,35 +654,29 @@ export class Transaction { } }).flat(); - // @ts-ignore - if (transactionJSONrepresentation.inputs[0].input.account_type === 'DelegationBalance') { - // @ts-ignore + const firstInput = transactionJSONrepresentation.inputs[0]?.input as any; + + if (firstInput?.account_type === 'DelegationBalance') { inputAddresses.push(transactionJSONrepresentation.outputs[0].destination); } - // @ts-ignore - if (transactionJSONrepresentation.inputs[0].input.input_type === 'AccountCommand') { - // @ts-ignore - if (transactionJSONrepresentation.inputs[0].input.destination) { - // @ts-ignore - inputAddresses.push(transactionJSONrepresentation.inputs[0].input.destination); + + if (firstInput?.input_type === 'AccountCommand') { + if (firstInput.destination) { + inputAddresses.push(firstInput.destination); } - // @ts-ignore - if (transactionJSONrepresentation.inputs[0].input.authority) { - // @ts-ignore - inputAddresses.push(transactionJSONrepresentation.inputs[0].input.authority); + + if (firstInput.authority) { + inputAddresses.push(firstInput.authority); } } - // @ts-ignore - if (transactionJSONrepresentation.inputs[0].input.input_type === 'Account') { - // @ts-ignore - if (transactionJSONrepresentation.inputs[0].input.destination) { - // @ts-ignore - inputAddresses.push(transactionJSONrepresentation.inputs[0].input.destination); + + if (firstInput?.input_type === 'Account') { + if (firstInput.destination) { + inputAddresses.push(firstInput.destination); } - // @ts-ignore - if (transactionJSONrepresentation.inputs[0].input.authority) { - // @ts-ignore - inputAddresses.push(transactionJSONrepresentation.inputs[0].input.authority); + + if (firstInput.authority) { + inputAddresses.push(firstInput.authority); } } diff --git a/packages/sdk/tests/staking-create-delegation.test.ts b/packages/sdk/tests/staking-create-delegation.test.ts index 139c336..c2bd1c3 100644 --- a/packages/sdk/tests/staking-create-delegation.test.ts +++ b/packages/sdk/tests/staking-create-delegation.test.ts @@ -46,13 +46,13 @@ test('create delegation uses a single coin UTXO to calculate the fee', async () destination: 'tmt1q9874wgx6enm2mzfu0yxhzleu84pp00l95l7er5z', }); - expect(tx.inputs).toHaveLength(1); - expect(tx.inputs[0]).toMatchObject({ + expect(tx.JSONRepresentation.inputs).toHaveLength(1); + expect(tx.JSONRepresentation.inputs[0]).toMatchObject({ input: { input_type: 'UTXO', }, }); - expect(tx.outputs).toEqual( + expect(tx.JSONRepresentation.outputs).toEqual( expect.arrayContaining([ expect.objectContaining({ type: 'CreateDelegationId',