'\n`;
codeTango += `);`;
@@ -52,6 +60,8 @@ function Hero({ network, setNetwork }) {
+
+
@@ -128,6 +138,14 @@ function Main({ network }) {
submitter={tangocryptoProvider}
submitterName="tangocryptoProvider"
/>
+
+
>
);
}
diff --git a/packages/demo/public/providers/ogmios.png b/packages/demo/public/providers/ogmios.png
new file mode 100644
index 000000000..d1cac84c8
Binary files /dev/null and b/packages/demo/public/providers/ogmios.png differ
diff --git a/packages/demo/public/templates/signin.png b/packages/demo/public/templates/signin.png
new file mode 100644
index 000000000..b24b819a4
Binary files /dev/null and b/packages/demo/public/templates/signin.png differ
diff --git a/packages/module/package.json b/packages/module/package.json
index 1e93bd3d3..85557dae2 100644
--- a/packages/module/package.json
+++ b/packages/module/package.json
@@ -3,7 +3,7 @@
"description": "Rapidly build Web3 apps on the Cardano Blockchain.",
"homepage": "https://meshjs.dev",
"author": "MeshJS",
- "version": "1.3.0",
+ "version": "1.4.0",
"license": "Apache-2.0",
"type": "module",
"repository": {
diff --git a/packages/module/src/common/constants.ts b/packages/module/src/common/constants.ts
index b7730ab46..93e50ce5d 100644
--- a/packages/module/src/common/constants.ts
+++ b/packages/module/src/common/constants.ts
@@ -80,6 +80,13 @@ export const SUPPORTED_HANDLES: Record = {
[csl.NetworkInfo.mainnet().network_id()]: 'f0ff48bbb7bbe9d59a40f1ce90e9e9d0ff5002ec48f232b49ca0fb9a',
};
+export const SUPPORTED_OGMIOS_LINKS: Record = {
+ mainnet: 'wss://ogmios-api.mainnet.dandelion.link',
+ preprod: 'wss://ogmios-api.iohk-preprod.dandelion.link',
+ preview: '__TBD_SOON_TM__',
+ testnet: 'wss://ogmios-api.testnet.dandelion.link',
+};
+
export const SUPPORTED_WALLETS = [
'begin', 'eternl', 'flint', 'nami', 'nufi', 'gerowallet', 'typhoncip30',
];
diff --git a/packages/module/src/common/contracts/evaluator.ts b/packages/module/src/common/contracts/evaluator.ts
new file mode 100644
index 000000000..843a2ecd3
--- /dev/null
+++ b/packages/module/src/common/contracts/evaluator.ts
@@ -0,0 +1,5 @@
+import { Action } from '@mesh/common/types';
+
+export interface IEvaluator {
+ evaluateTx(tx: string): Promise[]>;
+}
diff --git a/packages/module/src/common/contracts/fetcher.ts b/packages/module/src/common/contracts/fetcher.ts
index b9ee1126a..7d13d090e 100644
--- a/packages/module/src/common/contracts/fetcher.ts
+++ b/packages/module/src/common/contracts/fetcher.ts
@@ -1,5 +1,6 @@
import type {
- AccountInfo, AssetMetadata, Protocol, UTxO,
+ AccountInfo, AssetMetadata, BlockInfo,
+ Protocol, UTxO, TransactionInfo,
} from '@mesh/common/types';
export interface IFetcher {
@@ -9,6 +10,8 @@ export interface IFetcher {
asset: string,
): Promise<{ address: string; quantity: string }[]>;
fetchAssetMetadata(asset: string): Promise;
+ fetchBlockInfo(hash: string): Promise;
fetchHandleAddress(handle: string): Promise;
fetchProtocolParameters(epoch: number): Promise;
+ fetchTxInfo(hash: string): Promise;
}
diff --git a/packages/module/src/common/contracts/index.ts b/packages/module/src/common/contracts/index.ts
index 62b483630..499124d77 100644
--- a/packages/module/src/common/contracts/index.ts
+++ b/packages/module/src/common/contracts/index.ts
@@ -1,5 +1,7 @@
+export * from './evaluator';
export * from './fetcher';
export * from './initiator';
+export * from './listener';
export * from './signer';
export * from './submitter';
export * from './uploader';
diff --git a/packages/module/src/common/contracts/listener.ts b/packages/module/src/common/contracts/listener.ts
new file mode 100644
index 000000000..96b3bbc55
--- /dev/null
+++ b/packages/module/src/common/contracts/listener.ts
@@ -0,0 +1,3 @@
+export interface IListener {
+ onTxConfirmed(txHash: string, callback: () => void, limit?: number): void;
+}
diff --git a/packages/module/src/common/helpers/index.ts b/packages/module/src/common/helpers/index.ts
index 1711e50e2..c71746fe1 100644
--- a/packages/module/src/common/helpers/index.ts
+++ b/packages/module/src/common/helpers/index.ts
@@ -1,2 +1,4 @@
export * from './generateNonce';
export * from './mergeSignatures';
+export * from './readPlutusData';
+export * from './readTransaction';
diff --git a/packages/module/src/common/helpers/readTransaction.ts b/packages/module/src/common/helpers/readTransaction.ts
new file mode 100644
index 000000000..71a1e00fb
--- /dev/null
+++ b/packages/module/src/common/helpers/readTransaction.ts
@@ -0,0 +1,6 @@
+import { csl } from '@mesh/core';
+import { deserializeTx } from '@mesh/common/utils';
+
+export const readTransaction = (tx: string): csl.TransactionJSON => {
+ return deserializeTx(tx).to_js_value();
+};
diff --git a/packages/module/src/common/types/BlockInfo.ts b/packages/module/src/common/types/BlockInfo.ts
new file mode 100644
index 000000000..0f27c1fd2
--- /dev/null
+++ b/packages/module/src/common/types/BlockInfo.ts
@@ -0,0 +1,17 @@
+export type BlockInfo = {
+ time: number;
+ hash: string;
+ slot: string;
+ epoch: number;
+ epochSlot: string;
+ slotLeader: string;
+ size: number;
+ txCount: number;
+ output: string;
+ fees: string;
+ previousBlock: string;
+ nextBlock: string;
+ confirmations: number;
+ operationalCertificate: string;
+ VRFKey: string;
+};
diff --git a/packages/module/src/common/types/Network.ts b/packages/module/src/common/types/Network.ts
index 2660efa89..7b8b7ebb9 100644
--- a/packages/module/src/common/types/Network.ts
+++ b/packages/module/src/common/types/Network.ts
@@ -1,5 +1,7 @@
-export type Network =
-| 'testnet'
-| 'preview'
-| 'preprod'
-| 'mainnet';
+const ALL_NETWORKS = ['testnet', 'preview', 'preprod', 'mainnet'] as const;
+
+export type Network = typeof ALL_NETWORKS[number];
+
+export const isNetwork = (value: unknown): value is Network => {
+ return ALL_NETWORKS.includes(value as Network);
+};
diff --git a/packages/module/src/common/types/PoolParams.ts b/packages/module/src/common/types/PoolParams.ts
index 94986cf88..f9386f366 100644
--- a/packages/module/src/common/types/PoolParams.ts
+++ b/packages/module/src/common/types/PoolParams.ts
@@ -1,14 +1,14 @@
import { Relay } from './Relay';
export type PoolParams = {
+ VRFKeyHash: string;
operator: string;
- vrfKeyHash: string;
pledge: string;
cost: string;
margin: number;
- rewardAddress: string;
relays: Relay[];
owners: string[];
+ rewardAddress: string;
metadata?: PoolMetadata;
};
diff --git a/packages/module/src/common/types/TransactionInfo.ts b/packages/module/src/common/types/TransactionInfo.ts
new file mode 100644
index 000000000..8dac44cdc
--- /dev/null
+++ b/packages/module/src/common/types/TransactionInfo.ts
@@ -0,0 +1,11 @@
+export type TransactionInfo = {
+ index: number;
+ block: string;
+ hash: string;
+ slot: string;
+ fees: string;
+ size: number;
+ deposit: string;
+ invalidBefore: string;
+ invalidAfter: string;
+};
diff --git a/packages/module/src/common/types/index.ts b/packages/module/src/common/types/index.ts
index 58fb0fe4b..b94080197 100644
--- a/packages/module/src/common/types/index.ts
+++ b/packages/module/src/common/types/index.ts
@@ -4,6 +4,7 @@ export * from './Action';
export * from './Asset';
export * from './AssetExtended';
export * from './AssetMetadata';
+export * from './BlockInfo';
export * from './Data';
export * from './DataSignature';
export * from './Era';
@@ -15,5 +16,6 @@ export * from './PoolParams';
export * from './Protocol';
export * from './Recipient';
export * from './Relay';
+export * from './TransactionInfo';
export * from './UTxO';
export * from './Wallet';
diff --git a/packages/module/src/providers/blockfrost.provider.ts b/packages/module/src/providers/blockfrost.provider.ts
index b90320598..f0032ff4f 100644
--- a/packages/module/src/providers/blockfrost.provider.ts
+++ b/packages/module/src/providers/blockfrost.provider.ts
@@ -1,24 +1,32 @@
import axios, { AxiosInstance } from 'axios';
import { SUPPORTED_HANDLES } from '@mesh/common/constants';
-import { IFetcher, ISubmitter } from '@mesh/common/contracts';
+import { IFetcher, IListener, ISubmitter } from '@mesh/common/contracts';
import {
fromUTF8, parseAssetUnit, parseHttpError,
resolveRewardAddress, toBytes, toScriptRef,
} from '@mesh/common/utils';
import type {
- AccountInfo, AssetMetadata, NativeScript,
- PlutusScript, Protocol, UTxO,
+ AccountInfo, AssetMetadata, BlockInfo, NativeScript,
+ PlutusScript, Protocol, TransactionInfo, UTxO,
} from '@mesh/common/types';
-export class BlockfrostProvider implements IFetcher, ISubmitter {
+export class BlockfrostProvider implements IFetcher, IListener, ISubmitter {
private readonly _axiosInstance: AxiosInstance;
- constructor(projectId: string, version = 0) {
- const network = projectId.slice(0, 7);
- this._axiosInstance = axios.create({
- baseURL: `https://cardano-${network}.blockfrost.io/api/v${version}`,
- headers: { project_id: projectId },
- });
+ constructor(baseUrl: string);
+ constructor(projectId: string, version?: number);
+
+ constructor(...args: unknown[]) {
+ if (typeof args[0] === 'string' && args[0].startsWith('http')) {
+ this._axiosInstance = axios.create({ baseURL: args[0] });
+ } else {
+ const projectId = args[0] as string;
+ const network = projectId.slice(0, 7);
+ this._axiosInstance = axios.create({
+ baseURL: `https://cardano-${network}.blockfrost.io/api/v${args[1] ?? 0}`,
+ headers: { project_id: projectId },
+ });
+ }
}
async fetchAccountInfo(address: string): Promise {
@@ -147,6 +155,37 @@ export class BlockfrostProvider implements IFetcher, ISubmitter {
}
}
+ async fetchBlockInfo(hash: string): Promise {
+ try {
+ const { data, status } = await this._axiosInstance.get(
+ `blocks/${hash}`,
+ );
+
+ if (status === 200)
+ return {
+ confirmations: data.confirmations,
+ epoch: data.epoch,
+ epochSlot: data.epoch_slot.toString(),
+ fees: data.fees,
+ hash: data.hash,
+ nextBlock: data.next_block ?? '',
+ operationalCertificate: data.op_cert,
+ output: data.output ?? '0',
+ previousBlock: data.previous_block,
+ size: data.size,
+ slot: data.slot.toString(),
+ slotLeader: data.slot_leader ?? '',
+ time: data.time,
+ txCount: data.tx_count,
+ VRFKey: data.block_vrf,
+ };
+
+ throw parseHttpError(data);
+ } catch (error) {
+ throw parseHttpError(error);
+ }
+ }
+
async fetchHandleAddress(handle: string): Promise {
try {
const assetName = fromUTF8(handle.replace('$', ''));
@@ -199,6 +238,49 @@ export class BlockfrostProvider implements IFetcher, ISubmitter {
}
}
+ async fetchTxInfo(hash: string): Promise {
+ try {
+ const { data, status } = await this._axiosInstance.get(
+ `txs/${hash}`,
+ );
+
+ if (status === 200)
+ return {
+ block: data.block,
+ deposit: data.deposit,
+ fees: data.fees,
+ hash: data.hash,
+ index: data.index,
+ invalidAfter: data.invalid_hereafter ?? '',
+ invalidBefore: data.invalid_before ?? '',
+ slot: data.slot.toString(),
+ size: data.size,
+ };
+
+ throw parseHttpError(data);
+ } catch (error) {
+ throw parseHttpError(error);
+ }
+ }
+
+ onTxConfirmed(txHash: string, callback: () => void, limit = 20): void {
+ let attempts = 0;
+
+ const checkTx = setInterval(() => {
+ if (attempts >= limit)
+ clearInterval(checkTx);
+
+ this.fetchTxInfo(txHash).then((txInfo) => {
+ this.fetchBlockInfo(txInfo.block).then((blockInfo) => {
+ if (blockInfo?.confirmations > 0) {
+ clearInterval(checkTx);
+ callback();
+ }
+ }).catch(() => { attempts += 1; });
+ }).catch(() => { attempts += 1; });
+ }, 5_000);
+ }
+
async submitTx(tx: string): Promise {
try {
const headers = { 'Content-Type': 'application/cbor' };
diff --git a/packages/module/src/providers/index.ts b/packages/module/src/providers/index.ts
index f997f6ed8..d8bafb836 100644
--- a/packages/module/src/providers/index.ts
+++ b/packages/module/src/providers/index.ts
@@ -1,4 +1,5 @@
export * from './blockfrost.provider';
export * from './infura.provider';
export * from './koios.provider';
+export * from './ogmios.provider';
export * from './tango.provider';
diff --git a/packages/module/src/providers/koios.provider.ts b/packages/module/src/providers/koios.provider.ts
index 23ac5153a..d3bf0e4b9 100644
--- a/packages/module/src/providers/koios.provider.ts
+++ b/packages/module/src/providers/koios.provider.ts
@@ -1,23 +1,31 @@
import axios, { AxiosInstance } from 'axios';
import { SUPPORTED_HANDLES } from '@mesh/common/constants';
-import { IFetcher, ISubmitter } from '@mesh/common/contracts';
+import { IFetcher, IListener, ISubmitter } from '@mesh/common/contracts';
import {
deserializeNativeScript, fromNativeScript, fromUTF8,
parseAssetUnit, parseHttpError, resolveRewardAddress,
toBytes, toScriptRef, toUTF8,
} from '@mesh/common/utils';
import type {
- AccountInfo, Asset, AssetMetadata,
- PlutusScript, Protocol, UTxO,
+ AccountInfo, Asset, AssetMetadata, BlockInfo,
+ PlutusScript, Protocol, TransactionInfo, UTxO,
} from '@mesh/common/types';
-export class KoiosProvider implements IFetcher, ISubmitter {
+export class KoiosProvider implements IFetcher, IListener, ISubmitter {
private readonly _axiosInstance: AxiosInstance;
- constructor(network: 'api' | 'preview' | 'preprod' | 'guild', version = 0) {
- this._axiosInstance = axios.create({
- baseURL: `https://${network}.koios.rest/api/v${version}`,
- });
+ constructor(baseUrl: string);
+ constructor(network: 'api' | 'preview' | 'preprod' | 'guild', version?: number);
+
+ constructor(...args: unknown[]) {
+ if (typeof args[0] === 'string' && args[0].startsWith('http')) {
+ this._axiosInstance = axios.create({ baseURL: args[0] });
+ } else {
+ this._axiosInstance = axios.create({
+ baseURL: `https://${args[0]}.koios.rest/api/v${args[1] ?? 0}`,
+ });
+ }
+
}
async fetchAccountInfo(address: string): Promise {
@@ -143,6 +151,37 @@ export class KoiosProvider implements IFetcher, ISubmitter {
}
}
+ async fetchBlockInfo(hash: string): Promise {
+ try {
+ const { data, status } = await this._axiosInstance.post(
+ 'block_info', { _block_hashes: [hash] }
+ );
+
+ if (status === 200)
+ return {
+ confirmations: data[0].num_confirmations,
+ epoch: data[0].epoch_no,
+ epochSlot: data[0].epoch_slot.toString(),
+ fees: data[0].total_fees ?? '',
+ hash: data[0].hash,
+ nextBlock: data[0].child_hash ?? '',
+ operationalCertificate: data[0].op_cert,
+ output: data[0].total_output ?? '0',
+ previousBlock: data[0].parent_hash,
+ size: data[0].block_size,
+ slot: data[0].abs_slot.toString(),
+ slotLeader: data[0].pool ?? '',
+ time: data[0].block_time,
+ txCount: data[0].tx_count,
+ VRFKey: data[0].vrf_key,
+ };
+
+ throw parseHttpError(data);
+ } catch (error) {
+ throw parseHttpError(error);
+ }
+ }
+
async fetchHandleAddress(handle: string): Promise {
try {
const assetName = fromUTF8(handle.replace('$', ''));
@@ -195,6 +234,49 @@ export class KoiosProvider implements IFetcher, ISubmitter {
}
}
+ async fetchTxInfo(hash: string): Promise {
+ try {
+ const { data, status } = await this._axiosInstance.post(
+ 'tx_info', { _tx_hashes: [hash] },
+ );
+
+ if (status === 200)
+ return {
+ block: data[0].block_hash,
+ deposit: data[0].deposit,
+ fees: data[0].fee,
+ hash: data[0].tx_hash,
+ index: data[0].tx_block_index,
+ invalidAfter: data[0].invalid_after?.toString() ?? '',
+ invalidBefore: data[0].invalid_before?.toString() ?? '',
+ slot: data[0].absolute_slot.toString(),
+ size: data[0].tx_size,
+ };
+
+ throw parseHttpError(data);
+ } catch (error) {
+ throw parseHttpError(error);
+ }
+ }
+
+ onTxConfirmed(txHash: string, callback: () => void, limit = 20): void {
+ let attempts = 0;
+
+ const checkTx = setInterval(() => {
+ if (attempts >= limit)
+ clearInterval(checkTx);
+
+ this.fetchTxInfo(txHash).then((txInfo) => {
+ this.fetchBlockInfo(txInfo.block).then((blockInfo) => {
+ if (blockInfo?.confirmations > 0) {
+ clearInterval(checkTx);
+ callback();
+ }
+ }).catch(() => { attempts += 1; });
+ }).catch(() => { attempts += 1; });
+ }, 5_000);
+ }
+
async submitTx(tx: string): Promise {
try {
const headers = { 'Content-Type': 'application/cbor' };
diff --git a/packages/module/src/providers/ogmios.provider.ts b/packages/module/src/providers/ogmios.provider.ts
new file mode 100644
index 000000000..9cb2ca4c0
--- /dev/null
+++ b/packages/module/src/providers/ogmios.provider.ts
@@ -0,0 +1,122 @@
+import { SUPPORTED_OGMIOS_LINKS } from '@mesh/common/constants';
+import { IEvaluator, ISubmitter } from '@mesh/common/contracts';
+import { Action, isNetwork, Network } from '@mesh/common/types';
+
+export class OgmiosProvider implements IEvaluator, ISubmitter {
+ private readonly _baseUrl: string;
+
+ constructor(baseUrl: string);
+ constructor(network: Network);
+
+ constructor(...args: unknown[]) {
+ this._baseUrl = isNetwork(args[0])
+ ? SUPPORTED_OGMIOS_LINKS[args[0]]
+ : args[0] as string;
+ }
+
+ async evaluateTx(tx: string): Promise[]> {
+ const client = await this.open();
+
+ this.send(client, 'EvaluateTx', {
+ evaluate: tx,
+ });
+
+ return new Promise((resolve, reject) => {
+ client.addEventListener('message', (response: MessageEvent) => {
+ try {
+ const { result } = JSON.parse(response.data);
+
+ if (result.EvaluationResult) {
+ resolve(
+ Object.keys(result.EvaluationResult).map((key) =>
+ >{
+ index: parseInt(key.split(':')[1], 10),
+ tag: key.split(':')[0].toUpperCase(),
+ budget: {
+ mem: result.EvaluationResult[key].memory,
+ steps: result.EvaluationResult[key].steps,
+ },
+ }
+ )
+ );
+ } else {
+ reject(result.EvaluationFailure);
+ }
+
+ client.close();
+ } catch (error) {
+ reject(error);
+ }
+ }, { once: true });
+ });
+ }
+
+ async onNextTx(callback: (tx: unknown) => void): Promise<() => void> {
+ const client = await this.open();
+
+ this.send(client, 'AwaitAcquire', {});
+
+ client.addEventListener('message', (response: MessageEvent) => {
+ const { result } = JSON.parse(response.data);
+
+ if (result === null) {
+ return this.send(client, 'AwaitAcquire', {});
+ }
+
+ if (result.AwaitAcquired === undefined) {
+ callback(result);
+ }
+
+ this.send(client, 'NextTx', {});
+ });
+
+ return () => client.close();
+ }
+
+ async submitTx(tx: string): Promise {
+ const client = await this.open();
+
+ this.send(client, 'SubmitTx', {
+ submit: tx,
+ });
+
+ return new Promise((resolve, reject) => {
+ client.addEventListener('message', (response: MessageEvent) => {
+ try {
+ const { result } = JSON.parse(response.data);
+
+ if (result.SubmitSuccess) {
+ resolve(result.SubmitSuccess.txId);
+ } else {
+ reject(result.SubmitFail);
+ }
+
+ client.close();
+ } catch (error) {
+ reject(error);
+ }
+ }, { once: true });
+ });
+ }
+
+ private async open(): Promise {
+ const client = new WebSocket(this._baseUrl);
+
+ await new Promise((resolve) => {
+ client.addEventListener('open', () => resolve(true), { once: true });
+ });
+
+ return client;
+ }
+
+ private send(client: WebSocket, methodname: string, args: unknown) {
+ client.send(
+ JSON.stringify({
+ version: '1.0',
+ type: 'jsonwsp/request',
+ servicename: 'ogmios',
+ methodname, args,
+ })
+ );
+ }
+}
diff --git a/packages/module/src/providers/tango.provider.ts b/packages/module/src/providers/tango.provider.ts
index 6828d117d..b83227e51 100644
--- a/packages/module/src/providers/tango.provider.ts
+++ b/packages/module/src/providers/tango.provider.ts
@@ -1,17 +1,17 @@
import axios, { AxiosInstance } from 'axios';
import { SUPPORTED_HANDLES } from '@mesh/common/constants';
-import { IFetcher, ISubmitter } from '@mesh/common/contracts';
+import { IEvaluator, IFetcher, IListener, ISubmitter } from '@mesh/common/contracts';
import {
deserializeNativeScript, fromNativeScript,
fromUTF8, parseAssetUnit, parseHttpError,
resolveRewardAddress, toScriptRef, toUTF8,
} from '@mesh/common/utils';
import type {
- AccountInfo, Asset, AssetMetadata,
- PlutusScript, Protocol, UTxO,
+ AccountInfo, Action, Asset, AssetMetadata, BlockInfo,
+ PlutusScript, Protocol, TransactionInfo, UTxO,
} from '@mesh/common/types';
-export class TangoProvider implements IFetcher, ISubmitter {
+export class TangoProvider implements IEvaluator, IFetcher, IListener, ISubmitter {
private readonly _axiosInstance: AxiosInstance;
constructor(
@@ -25,6 +25,28 @@ export class TangoProvider implements IFetcher, ISubmitter {
});
}
+ async evaluateTx(tx: string): Promise[]> {
+ try {
+ const { data, status } = await this._axiosInstance.post(
+ 'transactions/evaluate', { tx, utxos: [] },
+ );
+
+ if (status === 200)
+ return data.redeemers.map((redeemer) => >({
+ index: redeemer.index,
+ tag: redeemer.purpose.toUpperCase(),
+ budget: {
+ mem: redeemer.unit_mem,
+ steps: redeemer.unit_steps,
+ },
+ }));
+
+ throw parseHttpError(data);
+ } catch (error) {
+ throw parseHttpError(error);
+ }
+ }
+
async fetchAccountInfo(address: string): Promise {
try {
const rewardAddress = address.startsWith('addr')
@@ -157,7 +179,38 @@ export class TangoProvider implements IFetcher, ISubmitter {
if (status === 200)
return {
- ...data.metadata[0]?.json[policyId][toUTF8(assetName)],
+ ...data.metadata.find((m) => m.label === 721)?.json[policyId][toUTF8(assetName)],
+ };
+
+ throw parseHttpError(data);
+ } catch (error) {
+ throw parseHttpError(error);
+ }
+ }
+
+ async fetchBlockInfo(hash: string): Promise {
+ try {
+ const { data, status } = await this._axiosInstance.get(
+ `blocks/${hash}`,
+ );
+
+ if (status === 200)
+ return {
+ confirmations: data.confirmations,
+ epoch: data.epoch_no,
+ epochSlot: data.epoch_slot_no.toString(),
+ fees: data.fees.toString(),
+ hash: data.hash,
+ nextBlock: data.next_block.toString() ?? '',
+ operationalCertificate: data.op_cert,
+ output: data.out_sum.toString() ?? '0',
+ previousBlock: data.previous_block.toString(),
+ size: data.size,
+ slot: data.slot_no.toString(),
+ slotLeader: data.slot_leader ?? '',
+ time: Date.parse(data.time),
+ txCount: data.tx_count,
+ VRFKey: data.vrf_key,
};
throw parseHttpError(data);
@@ -218,6 +271,49 @@ export class TangoProvider implements IFetcher, ISubmitter {
}
}
+ async fetchTxInfo(hash: string): Promise {
+ try {
+ const { data, status } = await this._axiosInstance.get(
+ `transactions/${hash}`,
+ );
+
+ if (status === 200)
+ return {
+ block: data.block.hash,
+ deposit: data.deposit,
+ fees: data.fee,
+ hash: data.hash,
+ index: data.block_index,
+ invalidAfter: data.invalid_hereafter ?? '',
+ invalidBefore: data.invalid_before ?? '',
+ slot: data.block.slot_no.toString(),
+ size: data.size,
+ };
+
+ throw parseHttpError(data);
+ } catch (error) {
+ throw parseHttpError(error);
+ }
+ }
+
+ onTxConfirmed(txHash: string, callback: () => void, limit = 20): void {
+ let attempts = 0;
+
+ const checkTx = setInterval(() => {
+ if (attempts >= limit)
+ clearInterval(checkTx);
+
+ this.fetchTxInfo(txHash).then((txInfo) => {
+ this.fetchBlockInfo(txInfo.block).then((blockInfo) => {
+ if (blockInfo?.confirmations > 0) {
+ clearInterval(checkTx);
+ callback();
+ }
+ }).catch(() => { attempts += 1; });
+ }).catch(() => { attempts += 1; });
+ }, 5_000);
+ }
+
async submitTx(tx: string): Promise {
try {
const headers = { 'Content-Type': 'application/json' };
diff --git a/packages/module/src/transaction/transaction.service.ts b/packages/module/src/transaction/transaction.service.ts
index 074b981d2..02b00e7c6 100644
--- a/packages/module/src/transaction/transaction.service.ts
+++ b/packages/module/src/transaction/transaction.service.ts
@@ -49,7 +49,7 @@ export class Transaction {
this._txWithdrawals = csl.Withdrawals.new();
}
- static maskMetadata(cborTx: string, era: Era = 'ALONZO') {
+ static maskMetadata(cborTx: string, era: Era = 'BABBAGE') {
const tx = deserializeTx(cborTx);
const txMetadata = tx.auxiliary_data()?.metadata();
@@ -88,7 +88,7 @@ export class Transaction {
return tx.auxiliary_data()?.metadata()?.to_hex() ?? '';
}
- static writeMetadata(cborTx: string, cborTxMetadata: string, era: Era = 'ALONZO') {
+ static writeMetadata(cborTx: string, cborTxMetadata: string, era: Era = 'BABBAGE') {
const tx = deserializeTx(cborTx);
const txAuxData = tx.auxiliary_data()
?? csl.AuxiliaryData.new();
@@ -328,14 +328,18 @@ export class Transaction {
if (amount.is_zero() || multiAsset === undefined)
return this;
- const txOutputBuilder = buildTxOutputBuilder(
+ const txOutputAmountBuilder = buildTxOutputBuilder(
recipient,
- );
+ ).next();
- const txOutput = txOutputBuilder.next()
- .with_asset_and_min_required_coin_by_utxo_cost(multiAsset,
- buildDataCost(this._protocolParameters.coinsPerUTxOSize),
- ).build();
+ const txOutput = amount.coin().is_zero()
+ ? txOutputAmountBuilder
+ .with_asset_and_min_required_coin_by_utxo_cost(multiAsset,
+ buildDataCost(this._protocolParameters.coinsPerUTxOSize),
+ ).build()
+ : txOutputAmountBuilder
+ .with_coin_and_asset(amount.coin(), multiAsset)
+ .build();
this._txBuilder.add_output(txOutput);
diff --git a/packages/react/package.json b/packages/react/package.json
index 471d54346..fff973fee 100644
--- a/packages/react/package.json
+++ b/packages/react/package.json
@@ -3,7 +3,7 @@
"description": "React Hooks & Components you need for building dApps on Cardano.",
"homepage": "https://meshjs.dev",
"author": "MeshJS",
- "version": "1.1.3",
+ "version": "1.1.4",
"license": "Apache-2.0",
"type": "module",
"repository": {
@@ -61,7 +61,7 @@
"vite": "3.1.4"
},
"peerDependencies": {
- "@meshsdk/core": "1.3.0",
+ "@meshsdk/core": "1.4.0",
"react-dom": "17.x || 18.x",
"react": "17.x || 18.x"
},