Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions packages/sdk/jest.setup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,10 @@ import fetchMock from 'jest-fetch-mock';
// import 'whatwg-fetch';
globalThis.fetch = fetchMock as any;
fetchMock.enableMocks();

import { TextDecoder, TextEncoder } from 'util';

Object.assign(global, {
TextDecoder,
TextEncoder,
});
3 changes: 2 additions & 1 deletion packages/sdk/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,14 @@
"docs": "typedoc --options typedoc.js"
},
"dependencies": {
"@mintlayer/wasm-lib": "^0.1.0"
"@mintlayer/wasm-lib": "workspace:*"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"@types/jest": "^29.5.14",
"@types/node":"^20.4.2",
"jest": "^29.7.0",
"jest-environment-jsdom": "^29.7.0",
"jest-fetch-mock": "^3.0.3",
Expand Down
23 changes: 18 additions & 5 deletions packages/sdk/src/mintlayer-connect-sdk.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2609,8 +2609,10 @@ class Client {
const transaction_id = get_transaction_id(transaction, true);

if (finalOutputs.some((output) => output.type === 'IssueNft')) {
const block_height = 200000n; // TODO: Get the current block height
const token_id = get_token_id(
mergeUint8Arrays(BINRepresentation.inputs),
block_height,
this.network === 'mainnet' ? Network.Mainnet : Network.Testnet,
);
const index = finalOutputs.findIndex((output) => output.type === 'IssueNft');
Expand Down Expand Up @@ -2675,14 +2677,17 @@ class Client {
.filter(({ input }) => input.input_type === 'AccountCommand' || input.input_type === 'Account')
.map(({ input }) => {
if (input.command === 'ConcludeOrder') {
return encode_input_for_conclude_order(input.order_id, BigInt(input.nonce.toString()), network);
const block_height = 200000n; // TODO: Get the current block height
return encode_input_for_conclude_order(input.order_id, BigInt(input.nonce.toString()), block_height, network);
}
if (input.command === 'FillOrder') {
const block_height = 200000n; // TODO: Get the current block height
return encode_input_for_fill_order(
input.order_id,
Amount.from_atoms(input.fill_atoms.toString()),
input.destination,
BigInt(input.nonce.toString()),
BigInt(block_height),
network,
);
}
Expand Down Expand Up @@ -4150,7 +4155,7 @@ class Signer {
(input, index) => {
let address: string | undefined = undefined;

if(input.input.input_type === 'UTXO') {
if (input.input.input_type === 'UTXO') {
const utxoInput = input as UtxoInput;
address = utxoInput.utxo.destination;
}
Expand All @@ -4168,24 +4173,32 @@ class Signer {
throw new Error(`Address not found for input at index ${index}`);
}

const addressPrivateKey = this.getPrivateKey(address)
const addressPrivateKey = this.getPrivateKey(address);

if (!addressPrivateKey) {
throw new Error(`Private key not found for address: ${address}`);
}

const transaction = this.hexToUint8Array(tx.HEXRepresentation_unsigned);

const block_height = 200000n; // TODO: Get the current block height
const additional_info = {
pool_info: {},
order_info: {}
};

const witness = encode_witness(
SignatureHashType.ALL,
addressPrivateKey,
address,
transaction,
optUtxos,
index,
additional_info,
block_height,
network,
)
return witness
);
return witness;
},
)

Expand Down
148 changes: 143 additions & 5 deletions packages/sdk/src/transaction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,11 @@ import {
Network,
TokenUnfreezable,
TotalSupply,
decode_signed_transaction_to_js,
} from '@mintlayer/wasm-lib';

import * as wasmLib from '@mintlayer/wasm-lib';

import { mergeUint8Arrays, atomsToDecimal, stringToUint8Array } from './utils';
import { UtxoEntry, UtxoInput } from './types/transaction';

Expand Down Expand Up @@ -103,6 +107,46 @@ export class Transaction {
return this;
}

enrichUtxo(knownUtxo: any): this {
const input = this.jsonRepresentation.inputs.find(
({ input }: any) =>
input.index === knownUtxo.outpoint.index &&
input.input_type === knownUtxo.outpoint.input_type &&
input.source_id === knownUtxo.outpoint.source_id &&
input.source_type === knownUtxo.outpoint.source_type,
);

if (!input) {
throw new Error(`UTXO input not found: ${knownUtxo.outpoint.source_id}:${knownUtxo.outpoint.index}`);
}

input.utxo = knownUtxo.utxo;

this.updateFee();

return this;
}

private updateFee(): void {
const inputAtoms = this.jsonRepresentation.inputs.reduce((total: any, item: any) => {
const atoms = item.utxo?.value?.amount?.atoms;

return atoms === undefined ? total : total + BigInt(atoms);
}, 0n);

const outputAtoms = this.jsonRepresentation.outputs.reduce((total: any, output: any) => {
return total + BigInt(output.value.amount.atoms);
}, 0n);

const feeAtoms = inputAtoms - outputAtoms;

if (feeAtoms < 0n) {
throw new Error('Transaction fee cannot be negative');
}

this.fee = BigInt(feeAtoms);
}

setNetwork(network: 'mainnet' | 'testnet') {
this.network = network;
return this;
Expand Down Expand Up @@ -133,9 +177,47 @@ export class Transaction {
}

fromHEX(hex: string) {
if (typeof hex !== 'string') {
throw new Error('Transaction hex must be a string');
}

if (!hex.length) {
throw new Error('Transaction hex cannot be empty');
}

if (!/^[0-9a-fA-F]+$/.test(hex)) {
throw new Error('Transaction hex contains invalid characters');
}

if (hex.length % 2 !== 0) {
throw new Error('Transaction hex must have an even number of characters');
}

const bytes = new Uint8Array(hex.match(/.{1,2}/g)!.map((byte) => parseInt(byte, 16)));

const network = this.network === 'mainnet' ? 0 : 1;

const decoded = wasmLib.decode_transaction_to_js(bytes, network);

this.hexRepresentation = hex;
this.jsonRepresentation = decoded_to_json_representation(decoded);
this.jsonRepresentation.id = get_transaction_id(bytes, false);
this.transactionId = get_transaction_id(bytes, false);

return this;
}

static fromHEX(
hex: string,
options: {
network?: 'mainnet' | 'testnet';
} = {},
) {
const transaction = new Transaction(options);

return transaction.fromHEX(hex);
}

getTransactionId() {
return this.transactionId;
}
Expand Down Expand Up @@ -417,14 +499,20 @@ export class Transaction {
.filter(({ input }) => input.input_type === 'AccountCommand' || input.input_type === 'Account')
.map(({ input }) => {
if (input.command === 'ConcludeOrder') {
return encode_input_for_conclude_order(input.order_id, BigInt(input.nonce.toString()), network);
return encode_input_for_conclude_order(
input.order_id,
BigInt(input.nonce.toString()),
BigInt(this.currentBlockHeight),
network,
);
}
if (input.command === 'FillOrder') {
return encode_input_for_fill_order(
input.order_id,
Amount.from_atoms(input.fill_atoms.toString()),
input.destination,
BigInt(input.nonce.toString()),
BigInt(this.currentBlockHeight),
network,
);
}
Expand Down Expand Up @@ -703,10 +791,10 @@ export class Transaction {
type: 'Coin',
amount: {
atoms: amount,
decimal: amount,
decimal: atomsToDecimal(amount, 11),
},
},
}
};
}

transferToken(destination: string, amount: string, token_id: string): Output {
Expand Down Expand Up @@ -736,7 +824,57 @@ export class Transaction {
delegation_id: '',
amount: 0,
},
}
};
}

}

const decoded_to_json_representation = (decoded: any) => {
const tx = decoded.V1;

return {
inputs: tx.inputs.map((input: any, index: any) => {
if (input.Utxo) {
return {
input: {
index,
input_type: 'UTXO',
source_id: input.Utxo.id.Transaction,
source_type: 'Transaction',
},
};
}

if (input.Account) {
return {
input: {
index,
input_type: 'ACCOUNT',
// TODO: map account input fields if needed
},
};
}

throw new Error(`Unsupported input type at index ${index}`);
}),

outputs: tx.outputs.map((output: any) => {
if (output.Transfer) {
const [value, destination] = output.Transfer;

return {
type: 'Transfer',
destination,
value: {
type: Object.keys(value)[0],
amount: {
atoms: value[Object.keys(value)[0]].atoms,
decimal: atomsToDecimal(value[Object.keys(value)[0]].atoms, 11),
},
},
};
}

throw new Error(`Unsupported output type: ${Object.keys(output)[0]}`);
}),
};
};
4 changes: 2 additions & 2 deletions packages/sdk/tests/__mocks__/pkg-node/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "wasm-wrappers",
"version": "1.0.2",
"version": "1.4.0",
"license": "MIT",
"files": [
"wasm_wrappers_bg.wasm",
Expand All @@ -9,4 +9,4 @@
],
"main": "wasm_wrappers.js",
"types": "wasm_wrappers.d.ts"
}
}
Loading
Loading