From 299bcb57a5b0c144c332695ddd6a07a6435281af Mon Sep 17 00:00:00 2001 From: Abdelkrim Dib Date: Thu, 29 Dec 2022 14:01:00 +0100 Subject: [PATCH 01/27] initial implementation for OgmiosProvider --- .../module/src/common/contracts/evaluator.ts | 3 + packages/module/src/common/contracts/index.ts | 2 + .../module/src/common/contracts/listener.ts | 3 + .../module/src/providers/ogmios.provider.ts | 104 ++++++++++++++++++ 4 files changed, 112 insertions(+) create mode 100644 packages/module/src/common/contracts/evaluator.ts create mode 100644 packages/module/src/common/contracts/listener.ts create mode 100644 packages/module/src/providers/ogmios.provider.ts diff --git a/packages/module/src/common/contracts/evaluator.ts b/packages/module/src/common/contracts/evaluator.ts new file mode 100644 index 00000000..9deafe2f --- /dev/null +++ b/packages/module/src/common/contracts/evaluator.ts @@ -0,0 +1,3 @@ +export interface IEvaluator { + evaluateTx(tx: string): Promise; +} diff --git a/packages/module/src/common/contracts/index.ts b/packages/module/src/common/contracts/index.ts index 62b48363..499124d7 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 00000000..3018a286 --- /dev/null +++ b/packages/module/src/common/contracts/listener.ts @@ -0,0 +1,3 @@ +export interface IListener { + onNextTx(callback: (tx: unknown) => void): Promise<() => void>; +} diff --git a/packages/module/src/providers/ogmios.provider.ts b/packages/module/src/providers/ogmios.provider.ts new file mode 100644 index 00000000..60ee1aa5 --- /dev/null +++ b/packages/module/src/providers/ogmios.provider.ts @@ -0,0 +1,104 @@ +import { IEvaluator, ISubmitter } from '@mesh/common/contracts'; + +export class OgmiosProvider implements IEvaluator, ISubmitter { + constructor( + private readonly _ogmiosURL = 'wss://ogmios-api.mainnet.dandelion.link', + ) {} + + 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 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(result.EvaluationResult); + } + else { + reject(result.EvaluationFailure); + } + + client.close(); + } catch (error) { + reject(error); + } + }, { once: true }); + }); + } + + 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._ogmiosURL); + + 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, + }) + ); + } +} From 2fb62b716569ce81dc1095a630b87c9810b4f6ec Mon Sep 17 00:00:00 2001 From: "Hong Jing (Jingles)" Date: Thu, 29 Dec 2022 22:52:36 +0800 Subject: [PATCH 02/27] test ogmios --- .../components/pages/providers/badges.tsx | 8 + .../providers/fetchers/fetchAccountInfo.tsx | 1 + packages/demo/pages/providers/ogmios.tsx | 143 ++++++++++++++++++ packages/module/src/providers/index.ts | 1 + .../module/src/providers/ogmios.provider.ts | 1 + 5 files changed, 154 insertions(+) create mode 100644 packages/demo/pages/providers/ogmios.tsx diff --git a/packages/demo/components/pages/providers/badges.tsx b/packages/demo/components/pages/providers/badges.tsx index e5a2fd25..f1b59f68 100644 --- a/packages/demo/components/pages/providers/badges.tsx +++ b/packages/demo/components/pages/providers/badges.tsx @@ -13,3 +13,11 @@ export function BadgeSubmitter() { ); } + +export function BadgeEvaluator() { + return ( + + Evaluator + + ); +} diff --git a/packages/demo/components/pages/providers/fetchers/fetchAccountInfo.tsx b/packages/demo/components/pages/providers/fetchers/fetchAccountInfo.tsx index 1294b3b6..0de5e34b 100644 --- a/packages/demo/components/pages/providers/fetchers/fetchAccountInfo.tsx +++ b/packages/demo/components/pages/providers/fetchers/fetchAccountInfo.tsx @@ -16,6 +16,7 @@ export function fetchAccountInfoLeft({ fetcherName, fetchAccountInfoAddress }) { ); } + export function fetchAccountInfoRight({ fetcher, fetchAccountInfoAddress, diff --git a/packages/demo/pages/providers/ogmios.tsx b/packages/demo/pages/providers/ogmios.tsx new file mode 100644 index 00000000..4e61936d --- /dev/null +++ b/packages/demo/pages/providers/ogmios.tsx @@ -0,0 +1,143 @@ +import { useState, useEffect } from 'react'; +import CommonLayout from '../../components/common/layout'; +import Metatags from '../../components/site/metatags'; +import Codeblock from '../../components/ui/codeblock'; +import { + BadgeEvaluator, + BadgeSubmitter, +} from '../../components/pages/providers/badges'; +import { OgmiosProvider, Transaction } from '@meshsdk/core'; +import Submitter from '../../components/pages/providers/submitter'; +// import ButtonGroup from '../../components/ui/buttongroup'; +import { CardanoWallet, useWallet } from '@meshsdk/react'; +import Button from '../../components/ui/button'; + +export default function ProvidersOgmios() { + const sidebarItems = [ + // { label: 'Fetch Account Info', to: 'fetchAccountInfo' }, + // { label: 'Fetch Asset Addresses', to: 'fetchAssetAddresses' }, + // { label: 'Fetch Asset Metadata', to: 'fetchAssetMetadata' }, + // { label: 'Fetch Address Utxos', to: 'fetchAddressUtxos' }, + // { label: 'Fetch Handle Address', to: 'fetchHandleAddress' }, + // { label: 'Fetch Protocol Parameters', to: 'fetchProtocolParameters' }, + { label: 'Submit Tx', to: 'submitTx' }, + ]; + const [network, setNetwork] = useState('preprod'); + + return ( + <> + + + +
+ + + ); +} + +function Hero({ network, setNetwork }) { + const { wallet, connected } = useWallet(); + + let code1 = `const ogmiosProvider = new OgmiosProvider();\n`; + + const ogmiosProvider = new OgmiosProvider( + 'wss://ogmios-api.preview.dandelion.link' + ); + + async function test() { + const tx = new Transaction({ initiator: wallet }).sendLovelace( + 'addr_test1qzmwuzc0qjenaljs2ytquyx8y8x02en3qxswlfcldwetaeuvldqg2n2p8y4kyjm8sqfyg0tpq9042atz0fr8c3grjmysm5e6yx', + '1000000' + ); + const unsignedTx = await tx.build(); + const signedTx = await wallet.signTx(unsignedTx); + const txHash = await ogmiosProvider.submitTx(signedTx); + console.log({ txHash }); + + // const res = await ogmiosProvider.evaluateTx(signedTx); + // console.log({ res }); + } + + useEffect(() => { + if (connected) { + console.log('load'); + ogmiosProvider.onNextTx((tx) => { + console.log(111, 'ogmiosProvider.onNextTx', tx); + }); + } + }, [connected]); + + return ( +
+

+ Ogmios + + + + +

+

+ Ogmios is a lightweight bridge interface for cardano-node. It offers a + WebSockets API that enables local clients to speak Ouroboros' + mini-protocols via JSON/RPC. +

+ + + + + +
+
+ {/*

+ + Blockfrost + {' '} + provides restful APIs which allows your app to access information + stored on the blockchain. +

+

Get started:

+ +

Choose network for this demo:

+ setNetwork('mainnet'), + }, + { + key: 'preprod', + label: 'Preprod', + onClick: () => setNetwork('preprod'), + }, + ]} + currentSelected={network} + /> */} +
+
+
+
+ ); +} + +function Main({ network }) { + const [provider, setProvider] = useState(null); + + useEffect(() => { + async function load() { + const _provider = new OgmiosProvider(); + setProvider(_provider); + } + load(); + }, [network]); + + return ( + <> + {/* */} + + + ); +} diff --git a/packages/module/src/providers/index.ts b/packages/module/src/providers/index.ts index f997f6ed..c5959021 100644 --- a/packages/module/src/providers/index.ts +++ b/packages/module/src/providers/index.ts @@ -2,3 +2,4 @@ export * from './blockfrost.provider'; export * from './infura.provider'; export * from './koios.provider'; export * from './tango.provider'; +export * from './ogmios.provider'; \ No newline at end of file diff --git a/packages/module/src/providers/ogmios.provider.ts b/packages/module/src/providers/ogmios.provider.ts index 60ee1aa5..a79110c0 100644 --- a/packages/module/src/providers/ogmios.provider.ts +++ b/packages/module/src/providers/ogmios.provider.ts @@ -38,6 +38,7 @@ export class OgmiosProvider implements IEvaluator, ISubmitter { client.addEventListener('message', (response: MessageEvent) => { try { const { result } = JSON.parse(response.data); + console.log({result}) if (result.EvaluationResult) { resolve(result.EvaluationResult); From e65556628df3e6c546f39ba75bac4f1873f23e7b Mon Sep 17 00:00:00 2001 From: Abdelkrim Dib Date: Fri, 30 Dec 2022 15:25:22 +0100 Subject: [PATCH 03/27] allow sending custom lovelace amount --- .../src/transaction/transaction.service.ts | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/packages/module/src/transaction/transaction.service.ts b/packages/module/src/transaction/transaction.service.ts index e2f3544f..71a749e5 100644 --- a/packages/module/src/transaction/transaction.service.ts +++ b/packages/module/src/transaction/transaction.service.ts @@ -320,14 +320,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); From 267cfd1641bd29e74a397ec63b7a74a6e74c2fc1 Mon Sep 17 00:00:00 2001 From: Abdelkrim Dib Date: Fri, 30 Dec 2022 15:26:12 +0100 Subject: [PATCH 04/27] switch to testnet --- packages/demo/pages/providers/ogmios.tsx | 3 +-- packages/module/src/providers/index.ts | 2 +- packages/module/src/providers/ogmios.provider.ts | 3 +-- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/packages/demo/pages/providers/ogmios.tsx b/packages/demo/pages/providers/ogmios.tsx index 4e61936d..5feee242 100644 --- a/packages/demo/pages/providers/ogmios.tsx +++ b/packages/demo/pages/providers/ogmios.tsx @@ -44,7 +44,7 @@ function Hero({ network, setNetwork }) { let code1 = `const ogmiosProvider = new OgmiosProvider();\n`; const ogmiosProvider = new OgmiosProvider( - 'wss://ogmios-api.preview.dandelion.link' + 'wss://ogmios-api.testnet.dandelion.link' ); async function test() { @@ -63,7 +63,6 @@ function Hero({ network, setNetwork }) { useEffect(() => { if (connected) { - console.log('load'); ogmiosProvider.onNextTx((tx) => { console.log(111, 'ogmiosProvider.onNextTx', tx); }); diff --git a/packages/module/src/providers/index.ts b/packages/module/src/providers/index.ts index c5959021..d8bafb83 100644 --- a/packages/module/src/providers/index.ts +++ b/packages/module/src/providers/index.ts @@ -1,5 +1,5 @@ export * from './blockfrost.provider'; export * from './infura.provider'; export * from './koios.provider'; +export * from './ogmios.provider'; export * from './tango.provider'; -export * from './ogmios.provider'; \ No newline at end of file diff --git a/packages/module/src/providers/ogmios.provider.ts b/packages/module/src/providers/ogmios.provider.ts index a79110c0..178b9137 100644 --- a/packages/module/src/providers/ogmios.provider.ts +++ b/packages/module/src/providers/ogmios.provider.ts @@ -16,7 +16,7 @@ export class OgmiosProvider implements IEvaluator, ISubmitter { if (result === null) { return this.send(client, 'AwaitAcquire', {}); } - + if (result.AwaitAcquired === undefined) { callback(result); } @@ -38,7 +38,6 @@ export class OgmiosProvider implements IEvaluator, ISubmitter { client.addEventListener('message', (response: MessageEvent) => { try { const { result } = JSON.parse(response.data); - console.log({result}) if (result.EvaluationResult) { resolve(result.EvaluationResult); From ee170a1146b49c1a97e631baf9f772ccd83e4ce1 Mon Sep 17 00:00:00 2001 From: Abdelkrim Dib Date: Wed, 11 Jan 2023 10:34:14 +0100 Subject: [PATCH 05/27] set BABBAGE as default era --- packages/module/src/transaction/transaction.service.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/module/src/transaction/transaction.service.ts b/packages/module/src/transaction/transaction.service.ts index 71a749e5..8892083e 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(); From 1a2a4d0823741a02257b3d86c1763c241dfd117e Mon Sep 17 00:00:00 2001 From: Abdelkrim Dib Date: Wed, 11 Jan 2023 10:34:44 +0100 Subject: [PATCH 06/27] update evaluateTx signature --- packages/module/src/common/contracts/evaluator.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/module/src/common/contracts/evaluator.ts b/packages/module/src/common/contracts/evaluator.ts index 9deafe2f..843a2ecd 100644 --- a/packages/module/src/common/contracts/evaluator.ts +++ b/packages/module/src/common/contracts/evaluator.ts @@ -1,3 +1,5 @@ +import { Action } from '@mesh/common/types'; + export interface IEvaluator { - evaluateTx(tx: string): Promise; + evaluateTx(tx: string): Promise[]>; } From 4568ad82a15a26e06818b1c9f02aef8469c8943f Mon Sep 17 00:00:00 2001 From: Abdelkrim Dib Date: Wed, 11 Jan 2023 10:35:28 +0100 Subject: [PATCH 07/27] add fetchBlockInfo and fetchTxInfo to IFetcher contract --- packages/module/src/common/contracts/fetcher.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/module/src/common/contracts/fetcher.ts b/packages/module/src/common/contracts/fetcher.ts index b9ee1126..7d13d090 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; } From bf65c0518e6a197dd50bf962a07ddba3072b606b Mon Sep 17 00:00:00 2001 From: Abdelkrim Dib Date: Wed, 11 Jan 2023 10:35:46 +0100 Subject: [PATCH 08/27] update IListener contract --- packages/module/src/common/contracts/listener.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/module/src/common/contracts/listener.ts b/packages/module/src/common/contracts/listener.ts index 3018a286..d4fcade5 100644 --- a/packages/module/src/common/contracts/listener.ts +++ b/packages/module/src/common/contracts/listener.ts @@ -1,3 +1,3 @@ export interface IListener { - onNextTx(callback: (tx: unknown) => void): Promise<() => void>; + onTxConfirmed(hash: string, callback: () => void, limit?: number): void; } From 6ae64437fc2cad2e4bea8cdb8328476d302c07fd Mon Sep 17 00:00:00 2001 From: Abdelkrim Dib Date: Wed, 11 Jan 2023 10:36:19 +0100 Subject: [PATCH 09/27] define BlockInfo type --- packages/module/src/common/types/BlockInfo.ts | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 packages/module/src/common/types/BlockInfo.ts diff --git a/packages/module/src/common/types/BlockInfo.ts b/packages/module/src/common/types/BlockInfo.ts new file mode 100644 index 00000000..0f27c1fd --- /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; +}; From 4075b2fbca37568801949d64050b519c0ab66a8e Mon Sep 17 00:00:00 2001 From: Abdelkrim Dib Date: Wed, 11 Jan 2023 10:36:39 +0100 Subject: [PATCH 10/27] define TransactionInfo type --- packages/module/src/common/types/TransactionInfo.ts | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 packages/module/src/common/types/TransactionInfo.ts diff --git a/packages/module/src/common/types/TransactionInfo.ts b/packages/module/src/common/types/TransactionInfo.ts new file mode 100644 index 00000000..8dac44cd --- /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; +}; From 5d72745206ce47c327fa37d62779cb8f3b602fd3 Mon Sep 17 00:00:00 2001 From: Abdelkrim Dib Date: Wed, 11 Jan 2023 10:37:03 +0100 Subject: [PATCH 11/27] export newly created types --- packages/module/src/common/types/PoolParams.ts | 4 ++-- packages/module/src/common/types/index.ts | 2 ++ packages/module/src/providers/ogmios.provider.ts | 3 ++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/packages/module/src/common/types/PoolParams.ts b/packages/module/src/common/types/PoolParams.ts index 94986cf8..f9386f36 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/index.ts b/packages/module/src/common/types/index.ts index 58fb0fe4..b9408019 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/ogmios.provider.ts b/packages/module/src/providers/ogmios.provider.ts index 178b9137..ba3aa8d7 100644 --- a/packages/module/src/providers/ogmios.provider.ts +++ b/packages/module/src/providers/ogmios.provider.ts @@ -1,4 +1,5 @@ import { IEvaluator, ISubmitter } from '@mesh/common/contracts'; +import type { Action } from '@mesh/common/types'; export class OgmiosProvider implements IEvaluator, ISubmitter { constructor( @@ -27,7 +28,7 @@ export class OgmiosProvider implements IEvaluator, ISubmitter { return () => client.close(); } - async evaluateTx(tx: string): Promise { + async evaluateTx(tx: string): Promise[]> { const client = await this.open(); this.send(client, 'EvaluateTx', { From 37366b455f0734437eff51de137720d8249a62a6 Mon Sep 17 00:00:00 2001 From: Abdelkrim Dib Date: Wed, 11 Jan 2023 10:42:08 +0100 Subject: [PATCH 12/27] implement new methods --- .../module/src/common/contracts/listener.ts | 2 +- .../src/providers/blockfrost.provider.ts | 82 ++++++++++++++++++- .../module/src/providers/koios.provider.ts | 82 ++++++++++++++++++- .../module/src/providers/tango.provider.ts | 82 ++++++++++++++++++- 4 files changed, 235 insertions(+), 13 deletions(-) diff --git a/packages/module/src/common/contracts/listener.ts b/packages/module/src/common/contracts/listener.ts index d4fcade5..96b3bbc5 100644 --- a/packages/module/src/common/contracts/listener.ts +++ b/packages/module/src/common/contracts/listener.ts @@ -1,3 +1,3 @@ export interface IListener { - onTxConfirmed(hash: string, callback: () => void, limit?: number): void; + onTxConfirmed(txHash: string, callback: () => void, limit?: number): void; } diff --git a/packages/module/src/providers/blockfrost.provider.ts b/packages/module/src/providers/blockfrost.provider.ts index b9032059..a2396301 100644 --- a/packages/module/src/providers/blockfrost.provider.ts +++ b/packages/module/src/providers/blockfrost.provider.ts @@ -1,16 +1,16 @@ 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) { @@ -147,6 +147,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 +230,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/koios.provider.ts b/packages/module/src/providers/koios.provider.ts index 23ac5153..c4247241 100644 --- a/packages/module/src/providers/koios.provider.ts +++ b/packages/module/src/providers/koios.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 { 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) { @@ -143,6 +143,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.num_confirmations, + epoch: data.epoch_no, + epochSlot: data.epoch_slot.toString(), + fees: data.total_fees ?? '', + hash: data.hash, + nextBlock: data.child_hash ?? '', + operationalCertificate: data.op_cert, + output: data.total_output ?? '0', + previousBlock: data.parent_hash, + size: data.block_size, + slot: data.abs_slot.toString(), + slotLeader: data.pool ?? '', + time: data.block_time, + txCount: data.tx_count, + VRFKey: data.vrf_key, + }; + + throw parseHttpError(data); + } catch (error) { + throw parseHttpError(error); + } + } + async fetchHandleAddress(handle: string): Promise { try { const assetName = fromUTF8(handle.replace('$', '')); @@ -195,6 +226,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.block_hash, + deposit: data.deposit, + fees: data.fee, + hash: data.tx_hash, + index: data.tx_block_index, + invalidAfter: data.invalid_after?.toString() ?? '', + invalidBefore: data.invalid_before?.toString() ?? '', + slot: data.absolute_slot.toString(), + size: data.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/tango.provider.ts b/packages/module/src/providers/tango.provider.ts index 6828d117..83e7b6d3 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 { 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, Asset, AssetMetadata, BlockInfo, + PlutusScript, Protocol, TransactionInfo, UTxO, } from '@mesh/common/types'; -export class TangoProvider implements IFetcher, ISubmitter { +export class TangoProvider implements IFetcher, IListener, ISubmitter { private readonly _axiosInstance: AxiosInstance; constructor( @@ -166,6 +166,37 @@ export class TangoProvider 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_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); + } catch (error) { + throw parseHttpError(error); + } + } + async fetchHandleAddress(handle: string): Promise { try { const assetName = fromUTF8(handle.replace('$', '')); @@ -218,6 +249,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' }; From 649c2ea8c82be1a5d2a12a86b1cb0763ce522af8 Mon Sep 17 00:00:00 2001 From: "Hong Jing (Jingles)" Date: Wed, 11 Jan 2023 20:23:44 +0800 Subject: [PATCH 13/27] update onTxConfirmed fetchBlockInfo fetchTxInfo --- .../components/pages/providers/badges.tsx | 8 ++ .../components/pages/providers/fetcher.tsx | 84 +++++++++++-- .../providers/fetchers/fetchBlockInfo.tsx | 59 +++++++++ .../pages/providers/fetchers/fetchTxInfo.tsx | 59 +++++++++ .../components/pages/providers/listener.tsx | 51 ++++++++ .../providers/listener/onTxConfirmed.tsx | 117 ++++++++++++++++++ packages/demo/pages/providers/blockfrost.tsx | 12 +- packages/demo/pages/providers/koios.tsx | 9 +- packages/demo/pages/providers/tangocrypto.tsx | 12 +- 9 files changed, 397 insertions(+), 14 deletions(-) create mode 100644 packages/demo/components/pages/providers/fetchers/fetchBlockInfo.tsx create mode 100644 packages/demo/components/pages/providers/fetchers/fetchTxInfo.tsx create mode 100644 packages/demo/components/pages/providers/listener.tsx create mode 100644 packages/demo/components/pages/providers/listener/onTxConfirmed.tsx diff --git a/packages/demo/components/pages/providers/badges.tsx b/packages/demo/components/pages/providers/badges.tsx index f1b59f68..2dcb760e 100644 --- a/packages/demo/components/pages/providers/badges.tsx +++ b/packages/demo/components/pages/providers/badges.tsx @@ -21,3 +21,11 @@ export function BadgeEvaluator() { ); } + +export function BadgeListener() { + return ( + + Listener + + ); +} diff --git a/packages/demo/components/pages/providers/fetcher.tsx b/packages/demo/components/pages/providers/fetcher.tsx index 54dd6b02..94c5ddba 100644 --- a/packages/demo/components/pages/providers/fetcher.tsx +++ b/packages/demo/components/pages/providers/fetcher.tsx @@ -1,4 +1,4 @@ -import { useState } from 'react'; +import { useEffect, useState } from 'react'; import SectionTwoCol from '../../common/sectionTwoCol'; import { demoAddresses } from '../../../configs/demo'; import { BadgeFetcher } from './badges'; @@ -26,8 +26,16 @@ import { fetchHandleAddressLeft, fetchHandleAddressRight, } from './fetchers/fetchHandleAddress'; +import { useWallet } from '@meshsdk/react'; +import { fetchTxInfoLeft, fetchTxInfoRight } from './fetchers/fetchTxInfo'; +import { + fetchBlockInfoLeft, + fetchBlockInfoRight, +} from './fetchers/fetchBlockInfo'; export default function Fetcher({ fetcher, fetcherName }) { + const { wallet, connected } = useWallet(); + const [fetchAddressUtxosAddress, setfetchAddressUtxosAddress] = useState(demoAddresses.testnet); const [fetchAddressUtxosAsset, setfetchAddressUtxosAsset] = useState( @@ -51,6 +59,26 @@ export default function Fetcher({ fetcher, fetcherName }) { const [fetchHandleAddressHandle, setfetchHandleAddressHandle] = useState('jingles'); + const [txHash, setTxHash] = useState( + 'f4ec9833a3bf95403d395f699bc564938f3419537e7fb5084425d3838a4b6159' + ); + const [block, setBlock] = useState( + '79f60880b097ec7dabb81f75f0b52fedf5e922d4f779a11c0c432dcf22c56089' + ); + + // useEffect(() => { + // async function init() { + // setTxHash( + // (await wallet.getNetworkId()) === 1 + // ? '84a1d1a9f8fb3e7b4f3d1bb04ece750fe2697e74b7916804c2f179870eb34f17' + // : 'f4ec9833a3bf95403d395f699bc564938f3419537e7fb5084425d3838a4b6159' + // ); + // } + // if (connected) { + // init(); + // } + // }, [connected]); + return ( <> } /> + + } + /> + } /> + } /> + } /> + } + /> ); } diff --git a/packages/demo/components/pages/providers/fetchers/fetchBlockInfo.tsx b/packages/demo/components/pages/providers/fetchers/fetchBlockInfo.tsx new file mode 100644 index 00000000..8cac96b3 --- /dev/null +++ b/packages/demo/components/pages/providers/fetchers/fetchBlockInfo.tsx @@ -0,0 +1,59 @@ +import { useState } from 'react'; +import RunDemoButton from '../../../common/runDemoButton'; +import RunDemoResult from '../../../common/runDemoResult'; +import Card from '../../../ui/card'; +import Codeblock from '../../../ui/codeblock'; +import Input from '../../../ui/input'; + +export function fetchBlockInfoLeft({ fetcherName, block }) { + let code1 = `await ${fetcherName}.fetchBlockInfo(\n`; + code1 += ` '${block}',\n`; + code1 += `)`; + return ( + <> +

+ Fetch block infomation. You can get the hash from{' '} + fetchTxInfo(). +

+ + + ); +} + +export function fetchBlockInfoRight({ fetcher, block, setBlock }) { + const [loading, setLoading] = useState(false); + const [response, setResponse] = useState(null); + const [responseError, setResponseError] = useState(null); + async function runDemo() { + setLoading(true); + setResponse(null); + setResponseError(null); + try { + const res = await fetcher.fetchBlockInfo(block); + setResponse(res); + } catch (error) { + setResponseError(`${error}`); + } + setLoading(false); + } + return ( + <> + + setBlock(e.target.value)} + placeholder="Block hash" + label="Block hash" + /> + + + + + + + ); +} diff --git a/packages/demo/components/pages/providers/fetchers/fetchTxInfo.tsx b/packages/demo/components/pages/providers/fetchers/fetchTxInfo.tsx new file mode 100644 index 00000000..77f533ba --- /dev/null +++ b/packages/demo/components/pages/providers/fetchers/fetchTxInfo.tsx @@ -0,0 +1,59 @@ +import { useState } from 'react'; +import RunDemoButton from '../../../common/runDemoButton'; +import RunDemoResult from '../../../common/runDemoResult'; +import Card from '../../../ui/card'; +import Codeblock from '../../../ui/codeblock'; +import Input from '../../../ui/input'; + +export function fetchTxInfoLeft({ fetcherName, txHash }) { + let code1 = `await ${fetcherName}.fetchTxInfo(\n`; + code1 += ` '${txHash}',\n`; + code1 += `)`; + return ( + <> +

+ Fetch transaction infomation. Only confirmed transaction can be + retrieved. +

+ + + ); +} + +export function fetchTxInfoRight({ fetcher, txHash, setTxHash }) { + const [loading, setLoading] = useState(false); + const [response, setResponse] = useState(null); + const [responseError, setResponseError] = useState(null); + async function runDemo() { + setLoading(true); + setResponse(null); + setResponseError(null); + try { + const res = await fetcher.fetchTxInfo(txHash); + setResponse(res); + } catch (error) { + setResponseError(`${error}`); + } + setLoading(false); + } + return ( + <> + + setTxHash(e.target.value)} + placeholder="Transaction hash" + label="Transaction hash" + /> + + + + + + + ); +} diff --git a/packages/demo/components/pages/providers/listener.tsx b/packages/demo/components/pages/providers/listener.tsx new file mode 100644 index 00000000..a415eb74 --- /dev/null +++ b/packages/demo/components/pages/providers/listener.tsx @@ -0,0 +1,51 @@ +import { useEffect, useState } from 'react'; +import SectionTwoCol from '../../common/sectionTwoCol'; +import { demoAddresses } from '../../../configs/demo'; +import { BadgeListener } from './badges'; +import { + onTxConfirmedLeft, + onTxConfirmedRight, +} from './listener/onTxConfirmed'; +import { useWallet } from '@meshsdk/react'; + +export default function Listener({ listener, listenerName }) { + const { wallet, connected } = useWallet(); + const [address, setAddress] = useState(demoAddresses.testnet); + const [lovelace, setLovelace] = useState('5000000'); + + useEffect(() => { + async function init() { + setAddress( + (await wallet.getNetworkId()) === 1 + ? demoAddresses.mainnet + : demoAddresses.testnet + ); + } + if (connected) { + init(); + } + }, [connected]); + + return ( + <> + } + /> + + ); +} diff --git a/packages/demo/components/pages/providers/listener/onTxConfirmed.tsx b/packages/demo/components/pages/providers/listener/onTxConfirmed.tsx new file mode 100644 index 00000000..7b10e2a5 --- /dev/null +++ b/packages/demo/components/pages/providers/listener/onTxConfirmed.tsx @@ -0,0 +1,117 @@ +import { useState } from 'react'; +import RunDemoButton from '../../../common/runDemoButton'; +import RunDemoResult from '../../../common/runDemoResult'; +import Card from '../../../ui/card'; +import Codeblock from '../../../ui/codeblock'; +import Input from '../../../ui/input'; +import { Transaction } from '@meshsdk/core'; +import { useWallet, CardanoWallet } from '@meshsdk/react'; + +export function onTxConfirmedLeft({ listenerName, address, lovelace }) { + let code1 = `const tx = new Transaction({ initiator: wallet });\n`; + code1 += `tx.sendLovelace('${address}', '${lovelace}');\n`; + code1 += `\n`; + code1 += `const unsignedTx = await tx.build();\n`; + code1 += `const signedTx = await wallet.signTx(unsignedTx);\n`; + code1 += `const txHash = await wallet.submitTx(signedTx);\n`; + code1 += `\n`; + code1 += `${listenerName}.onTxConfirmed(txHash, () => {\n`; + code1 += ` console.log('Transaction confirmed');\n`; + code1 += `});\n`; + + return ( + <> +

+ Allow you to listen to a transaction confirmation. Upon confirmation, + the callback will be called. +

+ + + ); +} + +export function onTxConfirmedRight({ + listener, + address, + setAddress, + lovelace, + setLovelace, +}) { + const { wallet, connected } = useWallet(); + + const [loading, setLoading] = useState(false); + const [response, setResponse] = useState(null); + const [responseError, setResponseError] = useState(null); + const [txWaitingConfrim, setTxWaitingConfrim] = + useState(null); + + async function runDemo() { + setLoading(true); + setResponse(null); + setResponseError(null); + setTxWaitingConfrim(null); + + try { + const tx = new Transaction({ initiator: wallet }); + tx.sendLovelace(address, lovelace); + + const unsignedTx = await tx.build(); + const signedTx = await wallet.signTx(unsignedTx); + const txHash = await wallet.submitTx(signedTx); + + setResponse(txHash); + + setTxWaitingConfrim(false); + listener.onTxConfirmed(txHash, () => { + setTxWaitingConfrim(true); + }); + } catch (error) { + setResponseError(`${error}`); + } + setLoading(false); + } + + return ( + <> + + setAddress(e.target.value)} + placeholder="Address" + label="Address" + /> + setLovelace(e.target.value)} + placeholder="Lovelace" + label="Lovelace" + /> + + {connected ? ( + + ) : ( + + )} + + {txWaitingConfrim === false && ( + + )} + {txWaitingConfrim === true && ( + + )} + + + + + ); +} diff --git a/packages/demo/pages/providers/blockfrost.tsx b/packages/demo/pages/providers/blockfrost.tsx index 0058810e..917901c4 100644 --- a/packages/demo/pages/providers/blockfrost.tsx +++ b/packages/demo/pages/providers/blockfrost.tsx @@ -4,22 +4,27 @@ import Metatags from '../../components/site/metatags'; import Codeblock from '../../components/ui/codeblock'; import { BadgeFetcher, + BadgeListener, BadgeSubmitter, } from '../../components/pages/providers/badges'; import Fetcher from '../../components/pages/providers/fetcher'; import { BlockfrostProvider } from '@meshsdk/core'; import Submitter from '../../components/pages/providers/submitter'; import ButtonGroup from '../../components/ui/buttongroup'; +import Listener from '../../components/pages/providers/listener'; export default function ProvidersBlockfrost() { const sidebarItems = [ { label: 'Fetch Account Info', to: 'fetchAccountInfo' }, + { label: 'Fetch Address Utxos', to: 'fetchAddressUtxos' }, { label: 'Fetch Asset Addresses', to: 'fetchAssetAddresses' }, { label: 'Fetch Asset Metadata', to: 'fetchAssetMetadata' }, - { label: 'Fetch Address Utxos', to: 'fetchAddressUtxos' }, + { label: 'Fetch Block Info', to: 'fetchBlockInfo' }, { label: 'Fetch Handle Address', to: 'fetchHandleAddress' }, { label: 'Fetch Protocol Parameters', to: 'fetchProtocolParameters' }, + { label: 'Fetch Transaction Info', to: 'fetchTxInfo' }, { label: 'Submit Tx', to: 'submitTx' }, + { label: 'On Transaction Confirmed', to: 'onTxConfirmed' }, ]; const [network, setNetwork] = useState('preprod'); @@ -46,6 +51,7 @@ function Hero({ network, setNetwork }) { +

@@ -111,6 +117,10 @@ function Main({ network }) { submitter={blockfrostProvider} submitterName="blockfrostProvider" /> + ); } diff --git a/packages/demo/pages/providers/koios.tsx b/packages/demo/pages/providers/koios.tsx index bf4eb12d..c1d644c5 100644 --- a/packages/demo/pages/providers/koios.tsx +++ b/packages/demo/pages/providers/koios.tsx @@ -5,21 +5,26 @@ import Codeblock from '../../components/ui/codeblock'; import { BadgeFetcher, BadgeSubmitter, + BadgeListener, } from '../../components/pages/providers/badges'; import Fetcher from '../../components/pages/providers/fetcher'; import { KoiosProvider } from '@meshsdk/core'; import Submitter from '../../components/pages/providers/submitter'; import ButtonGroup from '../../components/ui/buttongroup'; +import Listener from '../../components/pages/providers/listener'; export default function ProvidersKoios() { const sidebarItems = [ { label: 'Fetch Account Info', to: 'fetchAccountInfo' }, + { label: 'Fetch Address Utxos', to: 'fetchAddressUtxos' }, { label: 'Fetch Asset Addresses', to: 'fetchAssetAddresses' }, { label: 'Fetch Asset Metadata', to: 'fetchAssetMetadata' }, - { label: 'Fetch Address Utxos', to: 'fetchAddressUtxos' }, + { label: 'Fetch Block Info', to: 'fetchBlockInfo' }, { label: 'Fetch Handle Address', to: 'fetchHandleAddress' }, { label: 'Fetch Protocol Parameters', to: 'fetchProtocolParameters' }, + { label: 'Fetch Transaction Info', to: 'fetchTxInfo' }, { label: 'Submit Tx', to: 'submitTx' }, + { label: 'On Transaction Confirmed', to: 'onTxConfirmed' }, ]; const [network, setNetwork] = useState('preprod'); @@ -48,6 +53,7 @@ function Hero({ network, setNetwork }) { +

@@ -112,6 +118,7 @@ function Main({ network }) { <> + ); } diff --git a/packages/demo/pages/providers/tangocrypto.tsx b/packages/demo/pages/providers/tangocrypto.tsx index 90423df4..7228628e 100644 --- a/packages/demo/pages/providers/tangocrypto.tsx +++ b/packages/demo/pages/providers/tangocrypto.tsx @@ -5,21 +5,26 @@ import Codeblock from '../../components/ui/codeblock'; import { BadgeFetcher, BadgeSubmitter, + BadgeListener, } from '../../components/pages/providers/badges'; import Fetcher from '../../components/pages/providers/fetcher'; import { TangoProvider } from '@meshsdk/core'; import Submitter from '../../components/pages/providers/submitter'; import ButtonGroup from '../../components/ui/buttongroup'; +import Listener from '../../components/pages/providers/listener'; export default function ProvidersTangocrypto() { const sidebarItems = [ { label: 'Fetch Account Info', to: 'fetchAccountInfo' }, + { label: 'Fetch Address Utxos', to: 'fetchAddressUtxos' }, { label: 'Fetch Asset Addresses', to: 'fetchAssetAddresses' }, { label: 'Fetch Asset Metadata', to: 'fetchAssetMetadata' }, - { label: 'Fetch Address Utxos', to: 'fetchAddressUtxos' }, + { label: 'Fetch Block Info', to: 'fetchBlockInfo' }, { label: 'Fetch Handle Address', to: 'fetchHandleAddress' }, { label: 'Fetch Protocol Parameters', to: 'fetchProtocolParameters' }, + { label: 'Fetch Transaction Info', to: 'fetchTxInfo' }, { label: 'Submit Tx', to: 'submitTx' }, + { label: 'On Transaction Confirmed', to: 'onTxConfirmed' }, ]; const [network, setNetwork] = useState('preprod'); @@ -52,6 +57,7 @@ function Hero({ network, setNetwork }) { +

@@ -128,6 +134,10 @@ function Main({ network }) { submitter={tangocryptoProvider} submitterName="tangocryptoProvider" /> + ); } From a5543255cfe7af70e11bf939b29acdcdf3c7c6c0 Mon Sep 17 00:00:00 2001 From: "Hong Jing (Jingles)" Date: Wed, 11 Jan 2023 20:58:32 +0800 Subject: [PATCH 14/27] show ogmios --- packages/demo/pages/providers/index.tsx | 6 +- packages/demo/pages/providers/ogmios.tsx | 79 ++++++++-------------- packages/demo/public/providers/ogmios.png | Bin 0 -> 48986 bytes 3 files changed, 35 insertions(+), 50 deletions(-) create mode 100644 packages/demo/public/providers/ogmios.png diff --git a/packages/demo/pages/providers/index.tsx b/packages/demo/pages/providers/index.tsx index 38a4ac22..f68c240b 100644 --- a/packages/demo/pages/providers/index.tsx +++ b/packages/demo/pages/providers/index.tsx @@ -19,7 +19,11 @@ const ProvidersPage: NextPage = () => { link: '/providers/koios', thumbnail: '/providers/koios.png', }, - + { + title: 'Ogmios', + link: '/providers/ogmios', + thumbnail: '/providers/ogmios.png', + }, ]; return ( diff --git a/packages/demo/pages/providers/ogmios.tsx b/packages/demo/pages/providers/ogmios.tsx index 5feee242..035309f2 100644 --- a/packages/demo/pages/providers/ogmios.tsx +++ b/packages/demo/pages/providers/ogmios.tsx @@ -6,11 +6,10 @@ import { BadgeEvaluator, BadgeSubmitter, } from '../../components/pages/providers/badges'; -import { OgmiosProvider, Transaction } from '@meshsdk/core'; +import { OgmiosProvider } from '@meshsdk/core'; import Submitter from '../../components/pages/providers/submitter'; // import ButtonGroup from '../../components/ui/buttongroup'; import { CardanoWallet, useWallet } from '@meshsdk/react'; -import Button from '../../components/ui/button'; export default function ProvidersOgmios() { const sidebarItems = [ @@ -43,31 +42,31 @@ function Hero({ network, setNetwork }) { let code1 = `const ogmiosProvider = new OgmiosProvider();\n`; - const ogmiosProvider = new OgmiosProvider( - 'wss://ogmios-api.testnet.dandelion.link' - ); + // const ogmiosProvider = new OgmiosProvider( + // 'wss://ogmios-api.testnet.dandelion.link' + // ); - async function test() { - const tx = new Transaction({ initiator: wallet }).sendLovelace( - 'addr_test1qzmwuzc0qjenaljs2ytquyx8y8x02en3qxswlfcldwetaeuvldqg2n2p8y4kyjm8sqfyg0tpq9042atz0fr8c3grjmysm5e6yx', - '1000000' - ); - const unsignedTx = await tx.build(); - const signedTx = await wallet.signTx(unsignedTx); - const txHash = await ogmiosProvider.submitTx(signedTx); - console.log({ txHash }); + // async function test() { + // const tx = new Transaction({ initiator: wallet }).sendLovelace( + // 'addr_test1qzmwuzc0qjenaljs2ytquyx8y8x02en3qxswlfcldwetaeuvldqg2n2p8y4kyjm8sqfyg0tpq9042atz0fr8c3grjmysm5e6yx', + // '1000000' + // ); + // const unsignedTx = await tx.build(); + // const signedTx = await wallet.signTx(unsignedTx); + // const txHash = await ogmiosProvider.submitTx(signedTx); + // console.log({ txHash }); - // const res = await ogmiosProvider.evaluateTx(signedTx); - // console.log({ res }); - } + // // const res = await ogmiosProvider.evaluateTx(signedTx); + // // console.log({ res }); + // } - useEffect(() => { - if (connected) { - ogmiosProvider.onNextTx((tx) => { - console.log(111, 'ogmiosProvider.onNextTx', tx); - }); - } - }, [connected]); + // useEffect(() => { + // if (connected) { + // ogmiosProvider.onNextTx((tx) => { + // console.log(111, 'ogmiosProvider.onNextTx', tx); + // }); + // } + // }, [connected]); return (

@@ -84,37 +83,19 @@ function Hero({ network, setNetwork }) { mini-protocols via JSON/RPC.

- - - + {/* + */}
- {/*

- - Blockfrost +

+ Browse the{' '} + + Ogmios {' '} - provides restful APIs which allows your app to access information - stored on the blockchain. + website. Get started:

-

Get started:

-

Choose network for this demo:

- setNetwork('mainnet'), - }, - { - key: 'preprod', - label: 'Preprod', - onClick: () => setNetwork('preprod'), - }, - ]} - currentSelected={network} - /> */}
diff --git a/packages/demo/public/providers/ogmios.png b/packages/demo/public/providers/ogmios.png new file mode 100644 index 0000000000000000000000000000000000000000..d1cac84c86189accc8d02bb280d085b5bca145a9 GIT binary patch literal 48986 zcmZU)1yo(X@-}?n;9A_>i%W5Lio3hJyB90&Ufhej!=bnpcXxNE$V>0N|KGjq`}VAy zWG9(PGLyZMNuCp~ASZzchYJS)01%}lMU?;maLSK#0u0p0uOl270sw$uWho+}ASEI~ ztl(sCW@%#z07!->t3zuj4`XI)C&a}~f}_NtdZCGW#iI(t6BDb}pd<(ihclCuE-EVa zG)BUxxu}XxH3I5Nzjx9_bTVS1C%GzOi79%bp-#H$KdldQxW9StkNJ;e4R-4 zhlLseLl_5AJHNiA?IQBv11xw0yv+C+(XK6gLKij1u)@J6@xf{Z2XgrPCicvb3{CZb`IvMGQcf+bZxhom_QT`x$j>#Q(Vvq-2WhUu zzH}t=Q78qLeR2<GQ=caQB#|bCQGGsBB+HIMJb}jk#B z4Jlo9W598K_LC_POp8NchBO-WQB-2%c(oNCl2=1s6 zmq0W+5f?g8b{gqy8uVnO9YQMC07o>2Mj1?d5|p*?2)!NFYM>5jq_#~QE6C1Z#e~&G z%qg1AGf(n~YM|W1i^5aLaio<~ECH>&Zg3ou%Mh-UtIw|P#bZu~mj;`D&9^2;Csa00iN*INgID2PsOg>#BPx$At>lA_&J42;lt57Q~tWE#7yD ziwp-M`UD$bW1%+*Dh2F{yrz;=ULV7-K#AW#o0$Y1d^-3h83(-B1J?J6tS5k2OHp2X zd9c_Cqd1`1`yaa(kgd8cH5AZ_riY;USiV5hhm-Uf7;!QuS%l$jzqK$DLRJUXv~KFP zJQxseoWf4PRt8fAX9lzXS{u$6K0;QE0ke%0P*IJ%y|50{7Ljx3g?R`%jl(hD3D9WEAf7M@pB@~cQ-a=5%u(w8xl{rE(o`l8`LLfaJOiBng)(sS!E6^x1Q8q735ut zlek6p50Y0?luCDQbS}9u-_yu8@X9vYsV$K-lN3w;#Y+j_MF1falXk%N?P&XS8!lj5 zx8UWE*C4h>7;#kOhD!)C*roK*#fAM#apbo@4-U|)Z@L>U>~nz07@&h6iaf)(`msZQ z2;iN9ZA*?7KnVh0{N^WGknL|jtwSO zl*CW?R!EZm9Xp<^J`Pe6whos@qKQOxh;c}9m)r@=QtX^GHePO*{u;Jl;q%W(+=BT- z$ys>Qym$+6Zj^D+l6=TRVHYl~P_=yTDXm*_FWAoTro8G&LIi_jY#?hdJ~LxfbiYBF z1{7ugzCphRjavAe0pcZ>{@`*A!zr0u-w`L8doV30zI>nOqlynw7Xss;`%dGd41hp6?0;VW9E0`>p)q$}QZ5=IK6lyB;5ca_cjyaMuu_dY{ zyd`H*)P{ne;yzB29GCzcjzt&QPPmp7D?}{pEJQ1`E_73>&S=Xh%ZSt})%uyCQK?X= zVjH{c*l2CVIQu-LWkOqr%oRFDH9=fWd_l|+R~@%9?2B$2f!H6LAUK>qR+6M>MQu)v zOnossH#a`lIXCz-a`vDswTwtXp!!YYl}t6vO5RkNN?~tyZ&r7f@>utn2RGVG7QV1( z`r2aDQnf+3L83tqu`M(aN+PJ5S-n8GKy4>uz*@GhIGIw~Il(N@tZbiToHj#O%Y@#V zz6n#^Iz}yIUa@jTi*MQGOmUfcxpjH8;lV?q~%e8zU_Y<_0Sc}99Vvr0YBU8XuZkhuZ}UmVF8a))>a zb7zKW$FRcio1t{r96~$$Y0#U)tDtY7U+tUUYvywfLK+ka)Dl!V!Z89N?sn*Xs4-#* zjvYI%@!}U9_m&HX^~>!o=&%h|;po%o?I<%`TaGrHSz{X`d^0-cbpxM~meF8CREq@D zy7}D}sS=vC*$owww)xxOBk5wo#QrhE6lQtW_VK8(U*o%GD`Q$?&ttY@XyfqX519fD zxf&jtCK?ru7EOmuY>oKFT%&c%*V!0LBcop}8rP41%t2e@otT{T&%4eOus^foXM$&1 zWkNTxtpHZanv$D1PQSb9xh=V6o>nav+@IeI|AG5Mi&PpE7Bpm-C~^Yx3+4i48zq^{ zjz@_=Gv+B~E+#e$TTW3fM9x*NT0$#EhM?7Iw>4frYc0$9hdbv6Q4#BBBbBYvP(^#) zQ(X@R3B4!A-aF}_vUC6(%@YnHP zOC|XxvHdhe;HESh%V0*%ugveCa%CnCMXRUWH>{97on6!)L>(N6sl@)nYp1eW(c*k{ z`#5~%y}dyNK~W*ul4FrcEpHNBoAG<$ZTdi6xTP6Crw{5PDXegD7vrB_qgfm3@n=GMDM2lg^V!>cZ=ud37HZ z{Wa}q^~wEzT;%A-Z}8AtF6SLB&-BkQPSoVox(q~qoBWnzzRl{%nrWUso*{5@Wix+j z>m0{* z(ih8K^BW@yy`|=Hn@jI>b^ptDB*kxvB8r^Us}$WA%UO9-fjh!qdU6ph{EBu1?LX+|MQ{hML z)gV+)H!K<)j|Px4A2X|Lkydr;`#q$GQ{=V9v@dMnSJbpEbv4_vTKN3`+z?3d{C+*X zbDopy&7$DxK1IE-S|Mou0VQ7|cP? zZ*z~)vU`;q+q32=ZXqwDS1pW#Fpr3)O3cc9)@=dyWcgV6SUXl6-`4KZmh-xNrn((> z9G5och(O!%hm+6N)f*Zll~)G2tS%L+Jil%Bs?Ymui!z5?EZ>?F<@MN8eV(~|mT?x3 zgTqIx5+o- z9qW~auy=%)8>fes^LvhIj#vBCPF9cR6UOH53~wWE!p;fbjfcFJgfvTpvviL;AThoaR$c`yWaHZov9bNi;dFdV?e!>%AoJsT1lLNQw( z`qQJlhNm9>CuD$?LCIs3IR}^nfc?ZX4sV^7e+vy9?D&{_R)uub!Bx>oaZBp^4(vHaKN2@c+_)gpV?Skg|xB)JLpr z>||^pOGUAgSpL0AN!54Pa7A?84OMAjZ7Il zY#si}0q}Zoe9T?~mmY;EkExjpzu|5bzgBmFm8Hy`QeC(iFut&%(#_A#s9( zJ@doyGX7&TemGTVU)T>b;#-Q!t9-;Clk8v5-?>5k5&p&>L6-feux~g3APA5W6;km4 zJIxA9B_DQNukXa^u3yh==euwuh6x~s#f>AT`5L=DGt*s474PGPN)8C#TwB{R`2FY3 z&dklnyI=!LbxC_|X2;Fk-Q2y-&AYAbGc79%3+wji_n``v)ZP#>2jc%B11tw(jDTSV zix5HKO~HRCz>ye`IEexd{a;FbbA8E}kxz^`j zb=ohdb>%vpV4sfKYG(#l4~Gk?n7#;Q#1x?fz)JjUfS^u?k8XHx>3Og_Y&sqYf~Tq2 zuemv2_-$wC>p=P!>KrZA+r6wxsdF>U0U5#4c+Uw)XFodk=KR6-XJSV|coS4G8VJEu zwwmTz-~9@573e&jK57{4^jVkX^~5!(Lk4~|QH2HVPT3v{dTxU3wgdr`?L`#e1cAWz zcp$6+Y2@&kfY0tSk#2)S!iTzygCH|L)a8o=VQ_ueY*;F^33}ZD0hrRg56Bjt6hMV1 zb&R}6l-b-nBVolsv) zcbfH+>&ZZ(PgjbUHW!lF4-DrZseFc}49}8Lsof2UGr826RL@5g&dbNYDjDpK7C~$t z2`tRVDb`_c7pTC`K3drpc|zLX@kge0&f-0DV(FX^;Ye5rRpnNr$pC|}R$ zHM+jKr>7idNig;1PJDQ6G-<2Mj-2PL>>@RH?-({BTj@*_Cf;%u?tg3#* z?u|HVJMTTRJgO0nXk(_WgME`Mp&q=4XipYRw|OQSgDa z^@sF9J%jSlh7c}7Uhuvb3K$n7nfz*1q1ha zh7F}F%fsKhdAjZOlt?tT$|qOO;zVp#<_lGaJ6>HCy1LF)w5FpMBX>9)f7nMumP};3 zIii?J-VFh{s+H|(jq?Sf1|%F>PDeHZzMT;6p3^4o%W9olN@p>9aU&)=*-p2%gK_N3 za*AhaqhC~FF>}Y8!Eg%eagLQ^1mg|@Sz?|#vRH(dL4lnkA7A{v)o7U4gJW(qs&$ph znC?_d=b=_4Ekm@CR6!Rj^#qg^dq_TmOmzFNH=5KOY-cV%`$7i@QLA0m9Bqf_CyE7) z$`-b-0z2l5AwTxgbMW28uIWy58eWCwN0kG_UDUu}2+)lUak&RbDHb)f99@%5b0AaA z*mHVNr+SVRx&4NcV~JvBzA{VX3f;nfKTIEZ$+xbnw~nwqxU0CQvD{-3*N-l-tBBueQP{;7P z6srGsZUgaQKqdhsTL*;OfDyTrAh$}x2yr<7t%3>NyX7eBg58Lxj#1XciCm2oT-OCK zNl8z+AACpRa`XrD6{>ilaYyPaQ?ux z*J^R4R<}b5!VDRYbPM4mR`Z_R`yP2tpEq}mbAu0|5SQuXxRzs8w|-t3<4|Y`vH87M zYEPNvI7*+*fF*M!&-Xxn(NCigt6lZF1(_2&Fe&W`3PP; zGu?Tx>uJy~B3Y>JmrB)YA%l7)FPL;-g;Uoit}MW){itnHpWkaJI*03gAcyzp8t|PT zKFz+DWMf2s^XJ#s1A*4f&kJ^KvtP$Ol7r^yUVctXSn1NK&JK=nx{n+uL;ldne7?s| z@NYr?Sj8N|fM98>TVk9mBKVh82sD3MH+&DRsDERAZBr}bY3Yw`RjssO_!71em z3l@yn_%1p4|)huSbK^jWG&yYqDg}sx~8Fm*vN`?`rldO@o{iQ>N)t5e~o4 z&8@&!i3?h`hJ1nI5J`PO@_P~keqtZJKidj~#r$?W)}j5jWRLh5<$GqDI?y<~t+hAq zz6KXiIN#T#JeFxBuUveJFjLKca^HRZqg7^iZ%L|&_Zg`>{g7o(a7PmNi)HI#CTvz0(A)sC|jav7cNNrJI$%9)DvAy|FvYt*z&zKeAWO^V-Rv9*x& zR&vRB#B}iwqyVXuFt+(n1VFD5`?iy~c&iGyZBd0HAW-do`^iGD~vK{ zB=@g!Rp>^RD(0=x%Nm?o8nFezPx=`^ z2)D~6yjE|c%h()k^wq^B{CzSr!c_LBF6nzY>!p7Vp7%Ly^TjA*O9gt@v0&FLXjc}P zSa&Vcd6`6en&~pCuj2?=q3#rES^`F>GYSlL5oJh=C>N1NbeIMVndVu3L)fCjY?tEamCQlpa)~Au!wnahAz>08WM7bE%`&_RsUPGnhbs0JAZ zz9|j}vyL=xM<6&*jMkR)=6q~Rz$QLc4_>NY59^zP6{kgqcHIx*nWn{AnVK#%{op_N zkBjxa;1mGVJ+}4xo0^(hg{3B~3J!bdtakO-aT@hos;ffkUGL7Rt(~V4{rRI?=h#3C zYQ&j`FPdMOCu$*@Ls2gP3e(gz%oR||EtV=NNonqu)P$l2$M@au({lh)%~h#72x{G@ zLm>{wx2RU(YyL+kg{N>g^m>2@2CbqrSREh%sPEe!z5R9AkR~7VUO=k!I>+~qZ}ZD~ zNmb}vZlo)j1n<}o?Sug{SeimJQWZh{w;?eI1uf4V0`;d&c=et-;?$X0_=AELq>JJR zcm*EnNe$kXa0>Ehmedf#XZxhv zyB+$iW>#&ia$rFk>1mk=YK7FV1a_LffXY#g(G-D~UyYt!vIHzt$nCuwkEp~}q%5Dy ze)Qb8CdzWc<1fzqU4!H~-U-V)(2*s=(%Zg?VXb?`PPA7=h8VmoQLVDo-{PM`X!j&c zPI!Gi9zlm45xs)S^5~C2&sgPqi&Hg*-JIUjruF=qcHQ{3AeFM1WJGz?oJYgxvmIdo z8(F`PMNX#I6OQ}tNp=5-M!(jP8MET%9%6tzMx>0TN#B}VaX zC#)Ru0SF3T3xAWTI9D=;l5gdbLZzCkG?0*u1CC@ULW=7n3o|X?Luvj8Xy{L3sRy?G zWG!Xl-*XfLY6DS?7YROg>hcF8^b3#*n2vw&bI(k;P$Z{D(!Lv`oOR;H4<0va+a2vN zu8wG5Xd5`Zoie-cT35HCrr;7-5%QI9H^5n9NDq%xzi+lBwzO_8+@9Q`1ZvPxHDnWN zI)6X0?e9>Sk0yFsreoW_*y?@_?WZ)Y?K_g%V7F$0!2*ZA=ltt&?GPBqtLkfoi3p`f z=IA;lZ?K-cokmD!aUE0EB*}C?suZS68pC~_w;Of_QaSRcr03iXgq!@fM^&Eg`AN*$ zwF=w0^E9fGV2l z%4N1)Fp=#XR^xRGl20uYfUECX>rsG?a%Lk-y4xqQdH2e<|Jea#uu_k?%AX3EQMT!M@owFF@DOeXlA z|5%YwuttIJWiqIX3ZZbdoi~-+q0D>x<5(ufKn+8A({kixD$W}PnExzP$MJR{KYo@1 zgvnO<0XI)2jo%!3%oOBmde=Fjli5sz1u!IQN!x9^u4*CA49i;3#Bo9Uq@j$D46;Mu zSi^RtkB~|@89lsxHP?XJ(%J+O?tS=V-VIw1Mva{;lXXgv3QaZKyH>L5Z^qzqdkG{+ z!f;Z9p(7Hw^VXU)DbY#NENf6Q>JEDt010@7`q-B6ME`el#i&S{TNrF={=NWM%tVYN z-IP7hjJ`hx{OF7wgt{F134pIo=TJel5b#u4)D>Cr>1W<#CIH?&W>emX?mllenz8gc z&lS@nfU0g;rQwE_@#@6S#nSs?M3KgnC54J}sx$Gl@(Fb) zqQyhR5zE&SxgX+S);)$5Xk;a^%QbpJ+|0p< zdj4DZVh9PviLQ8f37NvD)aq9eGyhR2YmHAyZhVp9d>2nM zsY@VaY!ooefbeZ4m;l|kr4^e`>aOS1pj~Em0mwTFTwui{ZUMv|i}^c*mXO z>}&b#Ki9lWM>T<|-^5}Km}#OuWwJn|t-kr&x4@N~v{iI*J*(@xD)$ZEnqBdWyc5JO zaZ6`azg{A$_gW=T4(x-P!`(TZ$M!HT;iCDpUY*~x8@vsES!EM4ZM_wPL{06d#rdTO zYH~4@Mg@FV=D-w@;D@FJ{y0ta8_j*n3IYWKR_j*jATI3SWOyZejohBv1z0CZ;vDaD z-YLN;(epRz7*xY!TVL05R?O_qe2vc^_5J3e(df$mXhTwNXWl!vK1DXKRpz|U-`g)u zI9&$KO4kaWfKg98BX&oV;1$Dg0HNJDps}S-2(+On&CX4kZeyXt7abswcqNhgzM?}m zn)aOY&;y13#8)sWcU@Hr7>JD~jG`GkJm++U7#TArWbvY!;xg6d{mNO#{u)If*Lb{l z{NTUEU6+Ocj+PzIa*LS=2@ns7?rMXkTti7(C3i$-XX3y<{IZIL1u({_K;=r>X0CuR zu0yqqd_~M=_2Of?x)^yn+}^Lk*Z21we3D4*1ICXM1zDiXnC2@pz0#r7hS_*)Ad8bb7T%C1~1|L0z=Gk~2@o(w(*YV%0HU?P}TgT53{T2ftmmK9Asmue-uZV&omf`mS3Qq}%< zn<+c_iIXo;V9Q`vbU*nz_E!l7tZF&Td-iK1?td&dt*aquEvn-`6)vhdu^fKQ`28f^$fnJ2FPip zzR5-Ck3mqBbw6B?<(84^cWg%7CP2Yy*|6Zr(iOaQy5@-;EOH1>XeCqLDYL0+3_7)b zg-HqHicyns2fvCyfw}`=XGMjbKDQb0!w&reVnKMqXxG-p2B?|*jp<4GSF{!5_N+mZ zZ_u`5J%)m(b!z@&!&Ce?O-Gn`E|%~QWPQZgOp2XoSzcu9k}cLE)sav#m^k=i=K4aWO^g}9yfm#FF22)!K_FFjA_z#nKej+oC+T0agiqt$k)s|E9gPaNyaX9Y z1j3$|Go#z0tD{Y{gScD{WwnIT~Xqp6Z(+Pj6vTSW?I^z&S{)DnQ z`^QY_^gUnKqF$q>qhIu!blcMwAaSKC(sc*BmUo8^tIOQr*VlsZ>aM$1kpY$LWoWWnF!+mW_y!0f1~IB(Vmh;$n7C0{0?~NS`1PpK zV0M%PJt+a#YR&ZbVzzh;22OYWJ`(|ghE(*Fu#hC`0qjfDet4E{5#s=r&o<=6?mI3G^`FCx zNE?Vk_0~Lc$Pmn$UGeU2>MUx>PnuE*$Zvk8?$0cpC(GP6*%0r6i%g^HJFOpkQg zT<4b4*;}4 z-p&<^tCDU<+73y@Y9gjLF%;qXl3yRL#@YxN*pU&#g*bpR3oUGwnqd)GT`NrAlyOg) zu3}&f3QnK6BkJB#3qQQ56RrLQU#mrYrvf-`Z$#!zaWPf+?PZujcIf1ULNvgn`qm{u_Zoon+K7Qmd2CTOT7GYWrk|!`hcdA*_q_VWTH%Ik^_P%%~$s zSDY0?RWyhO&P~x;sK`@kmR>?Kf}{!ZQT`S18N=xd=^IH6iJf&DH=r)^C6@!Z+6t_M(6c2%zA3TDW z&oN+$xcmDltGpl6#X|vK@G7%ESkpZ$i({}R?Z@+d&N>E@;^yI;KQOIkD{Xz;=~&=3 z`E00Fk9fXVHHw~Mxmnnrx@#Q%Zu#_MqFz{fOiUPLcaQK> zHhm7;>z1g2I$C2y9y6*VtO;t$rj;mn^6=DqFurFFeB1r`BW9KF<7CtEPA^N0y?Q1R z<)(v{dhq@XQis(`J+8^5S=Y>zzE-DDQ+a<`vAyDLm!l)ZBZN=j9HM(<&5(|A4H(WG z{t%h|{#LzsWAfV-fHL^LO&d=e=A|v58LQu=8*O$BrEgbTesRj^f1uPCCR5|Ne#@a( zAN_FZEB4s?>Xx*HS& zL)$Kjurc=AChE3asoR)yGMq^>i7~37A7idn_rHs=-d*ZUIy-Ds^&w;;9Ubzz(Q36a zgOq9bYz{vfvQRP7nz^}P5;ZIyO$CV;6f+Wyb1(b^;?hx3`c0U&*zQylAxU@$8K~}m z8)~iFb+lXanVBrZpjo-nD6^oDX6=5gE#%@Ya#Ljo-E1zhZz|qT}$&Q{-m7{cw8D2!v zCE{JEbo0?Y___lL%eFDo3P(^;a*gliTXG_xA)nl;_v-B$GqWoF8a8((G{-fyuX}9Y zQ%(KIP=9${R&Y+N?ISUQM9QIzPF@QJurG9}x-@E{9H|GLmnCkz)ZvlT)COesKW80x zDEe7x`#&llw!fZJ*`>$+xs8wpevO#OZrrT0$ulUEEjt=J$Qmk{Fk)%Z!Or+%aX~pi z4vyhbzJa=}B-Q^Z@J{e2t%Bdf z;1?W4V`s)p_ub?2`*XbZKtCnNjG~)gmVvaFt%htw0RDVrtD*fJr9P0U_(ycS4i}y!Y)Z z-67s&yj`8XcEgTO)ptuxcGAaU%b|1?m8ed3(frC!R`SFCjb{pLm?c-fPFPK?1Hn36 zb%29(ozIW1D(Com4sE^$q{R*vchTRfUYrEz9UG8hL3JWB^)X|vYa-uqSUI>itO|bK z`Sh07?6!`mYx*wai6N3@%qFZ@lJgbt&VhmsMkJn&+Olq($2ZsVo*#}Y+H4ix$r-&4 zn0>V?-wpUUD&DzWF{8CD-))PhAmxsp7S+Q|t4q^)TA$EKP3FzCt!Xli47VWLl;K4!O*i+xTW-&3!yGMQ?FexP z$EDJAvTY13HN;m@^(Ay_9NSDe9Jm;n%1orvG$l327Xho5?(luQdA}#{Hq13*X;|~l zZhpPQA{lG+$^*x}4DL#k76T)$7HsYDG!@pRkaq$#8v(`6LRejZyt z@PG1d9x}6w8DDCEKQh#TcSDG=)m52OG54snT~38D>m0BvwX7iVyeFaVa#@#MgJe>i zAJDp1d*l9mx>I+11-bA-aB*#@c?WdgBM%H$oOO*1sDNXbccJzUgf`ig(zPsvpm~X>ASrfbkIgO zuO-xetHXvgP2d{V-d^?M=AD&zR*Q8~%{?PIe?if^aPeE+ zOR#I4%yC7yr<3>wWJ}6Jw;{lH+k6c9Nkze~MFbvMFSLr|>$)s!U9$ zPMcP3{gRWRj#=x8MzNmnbCtUvMrFiA4gC=qf?MCqw5PFSKfU4wjvBcc08{LkHAY_N zZafx+V>j92*5snvB{sz`QePTEn8TsRWKlu38u=3=Lg`Qcqx!Yp<`Q*>6<_{BWPFeB z_=k5MSvHgJ%}6r80zAHt<3#B7f+e1G+I8*UhhlAab%=gC0Qa_O&|EGTN-&xmJDe%e zjUTUeve`CcNLzd*j}!^>m6VD1a|v8CHrI(R0)@TrO4}uDDtFG9h468z0dQ0SvBs5t z*#Q%dpqQ}~OLoM1V2BYeLz--~k)y+@Wq}2kW z`D>(m3;yfAI{QWff1V8%f9D~lN3X-fuP>>g1FOTmRb$@Yu4nm1SmW$W-eJ*_`ZRgbR`n|57%S$~5kf@^V} zw!y?|6)tFgSuS?!tDr|2oJunUviGN4uGB`cfA}n4)27fQa>YDF5|_Vg&W}Hy{n}?~ zhc?KU9f1&>cw@E4;wNZz$`@C9Enrb+&~@hUfAw}N?1VhBREGe4*N0Sdw!~=(M6j67 zTST|TrBcr`613Xgn3ijr*Ve&gan>9CpSluK3XT1N)j z%~PX7C*-WD$B&B?;eUqedd}=Hs0_y-uh(%@O)`(+W3y>@1A_x^ER?jN^)&1GqE@`C zZXG;Zs0$zB3{d|L-g2u=>t}M;^46>0j=+<)uV6C%y3^76$O)DUT><`Cd6YkuzkQc` zH`S2#9$XD0EID@=p|hW^U55DlcEfLtodmB*$764>drOy#L)W%rH|7=69&29urLblm z@30Mv=>8k`DkUo&*V$^vSsnF^6*KgA?!$rKSq+Z_-Z+GUBIQxX0hLf-mAV09;aNr{ zj-K@If2ws7ZSU$Qel$HiC)JW4nz|0484;%_?!%v=cfti2<0R8=QPSMlI6C1A_J%rM z3a(t2BY~UM{fII$#2x(R{5ojAJZ;$dd2?6ap8y^+1>sy9gO}+E2QRAgrfl?=fOq@l zS{H2?d)&lZ`1`Z_={N6R_0-R5fk`Fos@`kU@Shyd&})e2;66ZO!n_00uMlk0FY|d> znvmNm!yN4@j$Fr@B?7Vr$r-<<6O7SX8)Cz?t1I)TdpAIVeB*(Y+A{ZSnZkQSJVwIkBU?kf@4Np_q^KezR)auLsFit z@Ss7PZ6~r8iXqmgVQI}ap|P-X*Pw%kr^Bj5A)86Zt;a@bO*~zcTNP`#G-of&8k9HfL^P_X#FvqgkR*isgw1TyT zJqhgR{SFPtqFgz#Hdi`pBv~K1Rw0l5mI5nWdb|V+%;2G;?9uXxv&Q`|E@$2LkHSqFQ0XTo0!XH0f@C&)QvPN)f|Q}RC*za_XO^)XRgjmQ8uLvedDdgGGpm$l|)mr5gZ943_nh? z3Gl$sC@wc4lp2#+b?oyTnHB^JY)fc z+q4=~#jr()U!i&}NN;Rm;9wPj_SnmSoNZ~3?E!E!;$R7M3S4wV{dqFI#^C&ZxuAy?tG)3hUrE0|^RQj6V8s5JjHaslD zs9f1V_uHxAdl1tGwEFRa{S4y=u+2#@V=bErGBP)GKtV|eXXW=8vaoWT*} zL%vr&@3CyXZRYV}SkRux#D=Ylv7~tn*_Q*)@}5s>I;BFBX1vkcTo#>qjbDCs8v6!w zN7${Q(c|26g|G75qIgg*%o*_9Un5fe#hHlwop%aN!I9nxKqWzHxj?v1m1=hfO-BeP zsarw75jzhAVFS8Gz+j%FD|Q^Q9mN@tvUPsc65dmblIMy_t%;wuoHZAO1V#u31qS?m zwuoe_G|Ovphbl0vK2Mkvdp))+{X)uwx;6>qle=X&YeED21cn%h3nsrJ@N30+GdjF*^t?XLI?r!zKB+Nah_aCMeY>AwR|*A4j$yNWansm0Z?;u$TtOe^H5i zlS6qCf35RxiiP_Nj#Sb5Joy(t`ERO${tJF1wAfYX|1a=TBIIMJyr1bY4iWyrW-k5< zNrcSiRBH4OuoG19>*KziFgXRz1qun`KXeNA7n%l*%ca8nPj@yI|AH1{ze+=L|F_@w zfRDp?CQbWM^DhXDWg0ixa2p04g)WQUUz69nuV$$bH+cF2+bzmv-OtzAdXT2xsLghs zI;S`#=SO41*9MgJku79@P}1l-6Gm@6UfY#V;95OwhL9j~?J5x&&)W{T+TZeZHT&BP z=s23Aan+J!a&&n5YjpE`pg4>>A);wl-K%q8Pu6jVehGX*Pa|jbghAZ2^d|30>_F|2 z@_F^N2sv7!E77luye|iyqehqe47=~W(3D12V;a6i(^nREQA7r9 z%=6h-tFm>=0_EObB0uF5ALaBKd~VfLDPm`+E~p4sZc4fc9%CLUQpzL-W$qu)qK@s$ zO?djlQ74eCf10e{@2_x1mad8thO-duTQ&m&MII*$7H+KCMR6yFGY{w)|5VDT=)H6* zW~)^#e|;~omoIr-UWmclbFO~iwZF}h8{E}~B<*_olbM3H(EmV`&ARTaDe=`);FPQ? za_UQP$`>a)$qZdjnkn(0E0bMWk_FtiZKb-gD=VK5@aTS)U0=KJ@VtrNj^FTc=&I7UZ(oz%;X0j(jOQjQW_<1Br1!jiuD5%-I;danxqya~ zi7Lx}G?6%Fj?TPtEwcTRQKf6&!my#M$86V`auOXvv7lAfmU7;>l4AThvG&4+;iPUa ze>6I}MW%LBniZ7zdrzk+%{^VXOFaf_(kiVq(8C0VuQTd?WY~)r+o^NLs@1G$zpaw} ztfzc)P7R-6xl&x)^`|S(gJl-z_h*EguM%?>(i_F@)|EEjK#_42ctB$mFBC z)s-Gvy;brCcTPS4D)xP7=&ljj+seu~A>D=9-h~{z=pqdM(JJQdQ>`x^D>kiiyhsU4 zk;r8-%X{JyI(9z6j*8nIr%qAGAOF`RR!fOr+JGAFIKS`U*oPkE>DEsEX-!wH)m_HO z(7rtO6520%H{9aFzEekLs?OUI%P-u?_M6Y&2Wj~Fr7`S^PcpT{uzB*}Nb`c{oT$yV zlT504uCm*{oRQsP6v+uhlrUv$BIs&{B`xd>Jburq-kG$*M>$Se+Sx!T+%?1YdWpcCgKN!XSmq+e{jX7!NpRNd=eq&=gafMdV)@&-&474rh0q=qIa0C7{- zt5wYQvBA>1+PUsSEfmcvQFFrTd1t?5FfC1oPt^+Ek4cj>Oj86%jj5nT84>3&JS2SM z5^19&`Vt3<2$12>lE6j^0yD{iB&;uOs>|?Uz;6=_{;|AbH7r_EofB=UyeIb zVv1>UX^;qWQyn~IBPE#=_jKD~zw;IRM#I6pf1PN2nTf-Ie7pQrCMbqCw`hSMrkahI zBKhl#8W#^1%o%J2d4Vc4Mxj6#|L~uOEuDr)B2-XN7*z8e{Zu=K*AOi!5q~eomArAG zZsn4@vET3)FdtlK@bhiwuzMU{69N>b;jf1PIi;cwTF?c!4;G^V&4D{lq|&`>cn#G`Hc211=b0>XqcGu{*)Q5hUkWzj5&LX1 zh;Br7oZ3)!epK+Lk)zGVl{+|>*iOI8yI@^^L&@Q?w)@3b)HVZ*TAId8Jc1JFvay`u zZMo|d!=GuWM59-PEz*`UJoDQvdqhY{`#|qw0!xfT;DgN7^GLbegm_2}4_73L`_B1{G_uFXqh$fHb|!ouK#i>sPfOLrU0=fWB|N z&a*67K#xD>tw5%zc3y`n<-k)*Jlr_xEU)iFXS_s7l=Q%NZC610;wNb8s*Tx6m!joM8bGL;st1F#R6hFsQ8!~|2?+2RC2 zav9t{AIxD6H^u(R@9w1S_I*cj?5qWr1~JO=LbyIFhrE#oESS(Wrl-wLC4*BYbbc*y zj5njBc&e!7Q3s$1uxJJ- zGnJt`>th=95i_Iu>zqrvtkU8FRd$lecg_*?+{@C@vnV$mInZAY!H{MamS(1vVgT5z ziw<9qn_>SxI{vsR6nj8_ROpZn)Vu#EorqUfr5^LFel2F^GW|*LG;iZJTV!^wc8Wjh zx@+0+a<5ZC2J7Tt71H%XJZzD?(I~^5@(i{+j=Nx8GJ#Krj7P{J8OxH{9a; zpSc|UN%av8c?NCiJj(y6GX{J$yAvfnz9i}5x=i48giht^Emr~LH0I`AdjZopGbL*m z+NSNt1$^;GTucA6_Z?*~&eH!8Tq-}0Fv&`w(DyU+^^xO;0V&7%95ep*Xxj##dShPC zP^f;lY7JKfR8^UH)Nan0>!~X%D9WwI*l)^u0{BubWC>HbzLkd6$L_@mqWUn=U@Yeu z$AvN6eki9L3TqU8kDNTh_?_wr+7+k2lW`$pf`-vR&h*wDQzn?#SvM3CLzFDzaj0X3tp4AQNO2b4(uF85AfJ7g+=) z2TA}IfztfsxXFezvv~@QKE7`Hy&DhF7tB)PsEovKXGr;+m0ug!ftGubOy@W z>y|n>9oo<_!bu%m=$URFea8$_{iLAuudjD+RLA&P<)%C}a8yo40-f*Vxv1`_t?1}d za8h=K15?)FCE(;z&;U^EijaA)@kQ99cKAV{w&LSwLUI=feapmNLm@Wj0Ffxkrd$V= zin$EbR+-qUB1LD}He*lUfPfZwDRZRq%~%^n3QJ}BJR5D?8=xSW zgG>7+oml{wHDwtbGW#lS#Y~fGGnt5m<|U;3u~TgZjp|2oJ4y*({$D?Ib8o%ta_V0k z>R%-U>Bvh@xsg|&cB23QneSD%UjK<{>Iox2dzD#H<-%3BjD@5DC~*iQDQ*c7&4|Qn z9o7zTWxYe?D80U;L;W8?16^t(Sy4LhW#sSyH;Tn!9A#NNAj=LWTv-k6uttr~hkRMq zcgDPC&FV)Is&dVkrRT~GQ)~jZDI0@;OXzCx5e;e%8{0A8hLmELR1c-Me{s3KE>d9J zuy&UvsC@3YD*;YQ2dAwrX0}!?D(pTT#%I-szA7u%$|slH$}fM9dEr_S-uhxR4@bEpq=usTJtV@o!6H1=!gOpT!y-UC^u zt)DABk7*1^uX%YO{+N^$&y#XttKBN*zuHpy>M?yxH*;pPgT5V&L&}BbG`v;3mw27U z6(@PCn3aO8+Q{Y%{Ldqs1yr_k>?|;ene02d{K-{1^U^05-SVfGT=mj5mxB3Xo&ZP_ z1=EB9N*@W{i1bMCa9#Pu%#uqW3vw%HHoDEDf%T=yM`aqP`O_0roLhOiM|qmjXvQm) zD8cCzAHZx}v&!*vC*9;TXKWz!nTkaZb$Aqxr0^8In(fORxO#oY+-NIbTs6~d3(Hj@ zjY5Yfa?NPQXH9{S@JiJLh_w-C`i2Kt+Np@}?Hd?dWQ_8YIalSGSyw!@zM^;*|jt=$m}) zIXCg8=iCT1d0=8?#C6lT%>FBw#g~|2H?PjPE}(s-UhVPScn%B1XK_NnqL?A(#xh#_ zM+#hmqb}hcKhqsld}=Zt6?oFjGlg(D@YR=b@0@mJ0!t%z7-GDgMacRS7u_7qkaYe52qV?ey7Usi6;A@?r0xu5>VTv+PQUCP$j z7{Q`fe)SoE$vHQE`dCC4)NjP+yP;>(XP}h^ura1_ruGwvs+cUe?p7~caZN1S8D{9J zo5p1g%c#}U=uLoq6-F9sPkr-K{Xb8eYMknPtB3MX|E+5RA{opP^?&jppGX#v2A(Qg z8w-FWr4f%CSW;~4pFN3dZ`zgRvZoK&LB66B4Ilk}n14Mj&@5MZ9UwCQ?k8^L(~GVS z7)h(oAfF|BHGVXD)VF$vRVC6}x+dA1O<~#K+so))+HIUj?@4DyGr+wqC@5hXg}!W0zMz#nF_ zK1@)nS8rg7#$_XO;PSF{esxkkrI~RwV~Wuj0!xU{IwqA6gOt6pR#2*D!3BgT55Xdi zxRGq2=$Ztm(E-~MX{2}?s|5c@VQ^h(@-WlWp$3o;H-?gjkfDU9uFD)K6ILE!qE#e0 z5V@3|KBYs`_|_N@MzgY2`66r=9-fhqW{R|mnO$Z=`7HU!XW3lFvL#;C2&keRGjwtZ z0kdGy7OI+=^{8jWBP_ZI4+$(27F^c~i4wj@1t{9Frd?o>Xo)QtCc><(Bvgnc!dxly zvCPGq2?&rD5axO$qC*OoL8C!R8<}-RgvIwYa79CYL@u3@OJQDAJcZ&AMu^eBCv!&zi-prqvnTGXY0> zn}v~&+Dv0lb<-GEo8_2k6`5K2I>@qO=cMy}L?-MS{j-bpL48OiA^hijm zE^_G=QA*=apLJtToFeFV)J)Pw_h5bO>3jQQatOLSFj@XM0wxu#3<71MP7|v#bdA3L zoEv{wzyunThuWtKkXlxZ6!Kc`TgCoViru4E(6`4?`h>^(4&BNFq!z!rQwBD*r^0{AqL9}dosC?=y*R3XmOCNk@9t8oE z8oDy#lCe7)J7kzeo=8WUT#k95b^(jogxMB!V7ZWNv@tY9&iXOd$A5V1$C+08MRJ3- zR-2HwCV)r@ll8`b%G9`1yGOb})RH#lX#p#iWN_xtKDY1HXWf22*8ZelBTI%bYDa*H z2o*sV6eUwMX1`VZs{$o5X(}L6j1&UaAw{kZt!M0@14pqR_`gdi6O>vJrA`82BdNfCNNr4pDC zi)Gb`6mG`T3te3V-Wb-CQOv|6Cke>ZYz+5~1mPWE6jf_i{PjE*r#M$sw^nmh%|uB1 zNSWX=kna-jNDrV{XGe&t_LJ^N>AfL zS;~SExjAI+<$2miG)>~uFVS`d1xV751S+G4^weD(;;r~2+T_al-#>D<-U67&Y!4;s z<5EOZfRaIAqP{5q@ruUDMF0uzjylT0D_R3(#H#M-2`n?3dlt~#LO}=>EEGP2at8e#&NwPoj ztdxg>@>Yq7S^46!t1{D5OpM}Sq@+}Wlvvb{+iq-4000d3NklMyG(F zC4ZTF1sZJ(tI{@}<36I_62cV%FL5mhpg2AX0Ip$KYD#%Rmz0(Itq$J@ z^KRn&naCB(HREi8zxL2Q{_%1!2~2MPtu}i z^LNS@4VxVI224~bF^3MLPCozMr=UJM_h*-^G7@}snf^qCXtCsUXb6|ZLWklyMgLS8LyO( z0Azu48yMTHmimdUv$w&38JGAZSrBX%$GHHeK$I;_rX7p)3(;)@S@hAbqCT&jq85{~ zl7K1#sK(38-cB8Hxu;IK>F1ubKrRg=4gOvOZ--{eR)O0ACQ|aENz3=<-hSWB{p$BF zC9p^v)Cpvh)yNLA7N}9r)3t&ubg`vwhG z_{en`X9D{2{hN{yMJOB1wq&glxOIyAiCv?%Dh{ioX0a9%gu7)}U6hRu+fG2d0=(Ag z3tFYL^zj8(zcFL05){WF3!Z3HKB7~AOJG_n?hb$J>t^DYOtCSlOyhd!^j;4uSNws8 za^=Wkva5iJwKKJh>n!)};zt+U;)kEP=B?W<1@Nmj=`SWTivLZC0yUB#b+99HxcJ4&|4`9?nfy;6tfVvOhExBcVd4Mle@%|ohc`JbV+l-z0 zKYVCoQ=q`cjqYn$^DUk$mIo+qdNR*q48?>MiK)>-jRG!L#noluuiC&Y`0)Q;4(SPj zKXm|EueAzDx>7n)6WX?7L-nCOGNkd=PYaoV-)alF@Cr;|bGTFY;mSVnKrAMShSUsP zOPJ&vYhWp<%RR6F+Cm`dpBQa3F$sT;E`wGpU`p~oDL{r#R*Z$o>cqB620xxrr0jG}(a$Yr5HhD#X6J0#au&fVfv38t zBTHG^po7jlv(^$|N)h&2*-&I}1v|u}Z4Sn5*K4x};&y5S9M!3W$g8rDFt{tTx1*Uf zX(WiC)@HUcgX_Elo6Gw!fieKpMqQzDTq8x?G+Ns(bErjXTf0R|cGcdhvy|Ii*ByFH zu&DnCy-Rr#?;?d~08;}5>zDSu&~iDy7lvki5~8Brg5vnLgM37fVuwcatlA*%r111< zci@$00a+tfmK2FS?4Q}{cihI?0VW2D&4+)@t$uNZz_uF^fKdGy)X`WG4>N2aHf@`T zN%-hoU{HZ(wnvX*Lj25gNIw%Vq~vA9<{0NYvV(0h*m|gRtfN2y-1)gt8O&51frrbFEc;BbFLNWGg>hr zrR9U-Y;P3+f-M1!`;rwtq%OVYP~Wh+Xm1y0%m%jRP39;TV&xj3i+-)G2F!ZGgUU{z zNUL^6o;l~Ha8XU3KTUH-ebo~53b?nET@6g)zN(pS6PYc`R+au&#eK63&stc}wBLx_ zvKm8@bLRTvTUWHCyo5Y3`G%a9Qq=fSI|?A1%UhF3fTwt>=QXZuWeoW#&#fLbndPBz zW&Jmn1-6d|ZLa-%)c%^u+xW*~C(B@!_!!CX6!Kj%O;$%uFwGhWE$ZI|z?1@I%gBN9 znWJvx)UinC$Qo|NTIpLQb$jX51{~_c6%_mmpinVo%eT;*)GGp1k)qe+y{*2>cpqWx z7h=$-eV=qq6Lc-5wtYsho|sjVI!Y#Nhu1?Z5RWt6ixx((l1@Bja%lZT-;%@dToyO8 znle$aKxu99LEFpqtW~es6NerUFcHxrSf*MKb`8O{_{jwe`b(*^(D8dN8`9Z~1tFIs zAWggbIVm-wb|q+KWl)?(w7d)EqC6t5QUaA*bHrt@xxUE--_iS$S?k>?M;%?unT5pF z$+c=|F850vGU^V@jK>X-5*vDd+I*^ ziR&gsq0Urgg^f(Li*J9xGHuW=gTR0mB#6A&ux$#wh+k04NF8k@>y-tLG22vdq(Gyh zukDBjnVl!=c$aMyWh#0ip8DTXuDv6^rmdRo1*yi8FvWtqUs{Rdf$NwWlg;Hthp)_VZVsUdKm3k!4(l+B-n8Rk~+~_^=6|dt4mCPgxI_iD?lySwS&r7Al^~!YSdcel4Y> z6>5slIQ-48*eVB^j|E^f(_ZEIRZP%L&b4x@Dr*nKnL^@un=Mg~E=o*s9wR3X6PLw4 zBe;fSM(@=lI@`29K3T7S#Qj17Q%51b0SxOd0;4Ct?Lw0J+?f=pX z?!cE{w73V0gBs+IFX%45cP%jSykaa^khPYgvml|)B&C7;uCc$3#fHS`#AD5RizDiq%&6=|P@Rzs?~Cw7FoJ@iq3`IXA`_(|D7s#OgDwY){$yJU!DuwoK5iLV20<%-reAetCB^r=Y8AiMU-mGkOzfQCpG52JaI#O=9q zU|b}~qjmZVT5SX3)67dgYn!F`G?XMJ%cPS-s9F#jX5o^Atjy4|ihxpW8K;n5Oj(kt zYdXEs3~7k?(e=OLPLWKYa(tPLLhH1;X0txc$`E)mTdA`Pe9gV{p=%KoR-n_!tzkiX z%8<5FK-QT2exSoLZk_3upElQq1mAt@=*h|?&!I!o2 z${9rf%DPy^yuZp!saj`gwk&pPE2@^mDp$m!)16{w54hd;+<61-vX1oPNMbfbjS=yK zzZ)N(he{Qni=CRa$$BG@aq~xSFoR~}JOU174mWng=OTT*ba21z!E!*ridl05pnmfw zZ@T4=zHk{$OaSW*CMlYk%A6bp>0+T`HH0iK+Q7{=E+l@$1it`}EImoOd<}Sh(}U+k zpPskWaeq*KqCRki`n<{Hf@Q6k_@e;HNEVfu9F0FqTnB=3{L~TK0>J1{-`|15`laY1 zzg^?Xp*_`KL|X%SF!#F;-EG{5*_9~Hs6s!KLiFunL5|`F7Sb}yu1AmqV_HVehV zOcrk}f_%~hgJjRx42yjp!{%f*=6`Bw@ze_O`0s zeczLrHPt=cJ?-68%#`WOtjvfLCr*5ET(m7y(q;;m#f5y%-Wg6)Z(mE}zx!jFzH>j- zCa2QU(o$M(OR0^2oxHrZ)Yp>+-v4Cke&TGZo;#H)r%t5K-tN>{t)#YgE-p%2{ylp@ zaWwlZ&aFJUv%gk1xp+o?ylCgbY%ML$%%%CM=```~wKRMEZmQoMN%gUbG(S6==9dBNiArM@RGNDy94ZQWVTH~zJKMCE;4ePSxj+_;-Ye)ESk zeeGtN8y<^3T8cI14rYGCmvwpW3 zKHZz=bXrlFy=dbjo+Im8r)79=vA&RMW0R>qI+p79M$;TTI(z$mnz?;9EzK`Pd&}(| zsq*Cc)cwKtrPCjOA(i`k_$~LKjalPXwCK@)SU=jmyttSa=IZI%zkV;>edTv)d0~M{ z`E$ks=d0&W2k)m^J=KQCQ;j+f`UWZQ>v+>*3kve<2Qt9Mi@N=oVE6v=x z7h}J6e$NO7lu|eOX$JnA{NsCR$X?YTwGTKtvzV7WweV_br8hGKE)c@2I!M}3C3YoaZ z{kME5r_&<+RG*kmV{iO9O}_bVntAs|@aV$gLYn8Cr5ze{IzH&@F8H+z8CDHhFqq0i z1E~YrHII`6mj%szMmw{nE~mxXJThQB&D|f3d206hoiuyvZu}i%joQVjj`hmtW(qdJXDN<_`X+f&Ce|8(tM2PE+XF+V!oV7b?9HGeT!7L zmw9mDnJ2TF6~BE%J=-<~OsIezrGQ_UnN4F1`pG}Mou=Po=fKuQ2i^bVxzn-72P!kSowsl|CP5hI^KGD2R@znYJicMoA=V>yVq0g zy_;$F9upscMPVduxDarHATJ?+yDI7I7eAg(zVyKWE7^dKyTz4z_NH*Au1oXtX$s*p zP9Kf@<_$p9ToRBdqGLcZ2<#^(zVcvy>UsaA)cf>B`srLM(QoYxkSP2n`lzVzVd^XD zWWO|chq_so7X>NvfU(=rS2OQjPYdG!X9i3g$IF1^{QxG}Bo$Q(dMX4v77?7(+40g2 zFmJOT;Fm50e-DGV@42T^KmAbZ@0Fc`uNqvFw=cWn&flHLqfnRH_;ecm{U6i4pZzw? z!{-Y%0C`SZ$8;NlT97ROpS}su<9x^hILi8U!Y|b-$Az!j66C1}BC zv}Kpukv*Vz20Tt5&mjD{HQVZ{QDqFv?XgtG={p!pYxM0C~u_cKVJzjHZwjJvVqG(9$NnZWXQrk+C22B zkEC8?py0ZV@fz*h1-=N<ELMIJE~QF4D-xNxuQ&C7J5S0 zyb|0}dx~eNIasAJ$tJ1nZZWx2r$SpLb?pZy{C0%k>Cb*F^}p{@s-jVBS4&m!N%6U* zN^Rj{X%+xwIZeECJq`c*jWqkGt7&cwtyagVv=xb_JXm7L+CUK>wCZ)pa z?$pQ80zg{onbzp{8Uix3o3KWidUXv9q!XWi zDfNHg>C|)Kbk-Jr)u8(Z&)Dcx*5`)+n8=s>x1Z{afzj9AOk=K9|BdhbG%Yi6 zFR`dsCLjf3%#>KXpZoHsQa@v;%;F^YiQn#&FQWqc@s_vM#zw1oi^Zdh8|K8VluZK@ zRb&$&&-*`p71Vtf^UX99a;EZCaFys>)xN3$2VQuV6(CxY)0l1mDi0C1!R%Y9>H%F_ z5W9Fhf2*d|OfrqCcoLe--Wy2^xv+N9=|F&Yv-;44)$_vpQy;6~ZdCJ)vPbRLe50md z6yP+C%FqEBz%gSJ%nI$P(%;7fIFzcWFMCiK_EJYpZV#CZ_Na~x8tlcFRej~8yMU@x z(_8J%NUB}Cm6qgH(EEuGKO5R12fvz&Z3$hU$Nkna&)rsa2~C^rz~D zGhw9<3g>Wpfs=UBnguZ#8{d{~0t%9xdu*?mmL z{m;KIzz< zIl#UEf{icRc>aF>ByaDE$8Mwk8v~Pqs1R_PiFk|^$o-%EI+$B6foMpTbSJC73ajwa z#WSfFEy>wWf9!!DiTAK#+o;@%#vSw@1;O+7XiJ-A;vfFmt7!rOsHrDt66dkzpqi`n z^rUodD4j+{IP{@s!cL1s-+fx}OoieE+N{yv|0#|B@^_i90LPqMoJ7LJRz+Qls=fD_ zOR-w4GKq9zcH)v4An|t8`$;~rK}E^iqj_hJ72^ULBqyLA2EUp-uiW;86oh*;Fe!)| zlXcxDPPy7clP8`RAEBz3z{p+xO6*%~KytIjr7Fnkol- z(<#Q~`7eDkfK>3n4(od;fJx5#i)ip>@U6K0?VqK&8+SsJs@dH-+Ey1SE>K*Y^wEc! zal2SNMH`}@52*j-o82rqJ(kdt)bKq~dphx__tG>zl7f>(07IC#;p@;Iv5+1>i+ujC zJ{RLu)89^tW6NSy`zm5p`R_G{A({fg|B59LoCX)Cwx{ zhOpn^VBab&_T&0cf9Jq#@(R8jzyEU@!KOXGAhTs=oenifqHtEn+AdFkz`JoURWq@p z$#9JZI;p79Qbjm|4RZ<2i5i^fBj%An&o43GLEfe-Y#}(m@Y1U@tx~gJ??1*y1(UHp)S@p-}<8xM-jr zn(*%Y;&(9_)R+uv`Jw^z5J1uKc>b%O3CFx}g3bH0`Bq!`exSf4=W+cf7VxPVe&zKv z@~hWV4ZpzodgcpgThx@4E7dR|p1_oK@+C|bnhji?TMk42Wi{E|cl$Ok`kvj#pK1aP zhVpU&?V1kKYM1r9sSue{C7R5Iwhrk=mdP_wwn(1_FqsyZyqc!*O{}BAnlos`g%Uhz z83H6e`Cq?^X^p7W)e&rQ#pCt1HZYON?MPnZf4maj`ZatPMBd>P$EglFNT(tFDD){g zLljD}`t!HPdKE5@NFxNg19}z{!7!_n8CIGzgs0UI1Twipy@WGtJF5{xK+k{ub3rd7 zU;KTPEi!ucBi?;}UWX}sU2c5$r!f4zG>^7q9>*?kFL107#@xx|p)>A)Z=zIyjEULR3?Uu;gXI)^zv!rn)H|v#Am!JH^3u)*hABf4*svL@_G}~ca z;OA+~9-|2D`}k%Eu+jg;IU`y+aS-gpKj-vkUrGaj5TzT+sP6pP;33}YL z229X~4=cMIm(DRI0Q(?|sgs|3DRcg2GTN!!^7jtBgV2~X+KdUu$f=Q^yqd=FHB%P2 zND-Q=wG$v4eRC4$UngCI6QbU9TLYXNd>8Ib4H|sD6p>qMlV$Pj@g882+OJ*%E|Q9}+8Z)uDJ18~<(0bx5h0>vHsc94Xe^ zrjSDnc^sK!?7Mh02SlYRUJzS!>7AI-G-^2Fj|S#W<(D#2(|+Z8iIs-*9e6* z2gNN^)^)Tu3#@t-LITGQ2AFEW6NGG^c=>~=7a)8^0zH9u!j7&~dtK?0Ll ziO{w={3!0=TsiX=es2I2C%|lxgmxRvNagg&)Wss@%x69dPomw%r1MzG0(e6J?)Zxk z>R3>|jd^Dj;EIDZ91=kSM3{C$pMJEP7ryq{%*-W^Z;{uFalU}Ye=>h1EKuPUBp)8p4iL6JRp zv+wDHwSb9FGoh9%^DeZCeP|f;^f#X0zRxB|FFGWXdd|m&JrA?ezVWRer)7lcB4O8# zdlQ3|$Vbp|dN5u5`sYJq5*kD6e@L3p=;6Fy1SYcrlOO#$4gV4!upF4EIS`n1J$WJZ zup&Om!fH1h4jbbVTJ2kcwY=YKyFNlX{NBWWqDIO9{TfW5=7=|By^Jr7K7d1*uzE2s zoA@ruVd257HhcTdz_Xuhvd-Qn47~TFUqyta0Syaim>irD22a?#n!&SZx+BrqcG5oN z^Qp-??*sTJ@kgA|@fN_SKcWva&4#{O10>xWeCBd!wOw3pcYfGg0`AY<9|9%{8_OaZ zfIIk=sCufKJ#T?YFV_H6JIkqu=#G9S*g>Lj%)byqN;TKx#N-mH5Dc{2ESg5f-2C=W zBI})rstOI0g28BjldLvRp+>C|wj0i7-eZe2D+Hi4zxn;2r}5wZF)b3FI0q1l!xg7=C4>&pQg+liX++~X>U_Jj@S59#?RU~&S#l0jBO zJ!no2H89DEH1fN*82dj=ivkl?;pQ$0U;?OWM+gwfL>6ISD?FHtJV>_Q4?U0`Ba5Zsj|~yIJ~{d_X3SvJy${C=6X3p zxTu*&3;J!U#m&#bmOM_D&T*p-=%ZP}&*Wd9Q^w)8az4u`bN(+r*-+Vv!dqn*DX>{! zqKZ~vLiE~^04C&GxPi~o!fIe*NGZwqF8{s2Eh>7nO6W%?1Y1DMhqX}?=jc6);+}Mlh^jAI^sn@H>ZvjgP3~2FK z7RN3{+SOS=jsi^X{O8ZZ{I!7QSM zetP$QW70FbYZs1uWz((W6m%A~lO1b`&Wdl3;hQw@1=DB(Gx6HJ@BNZg&~KqJ$(X#2 z)3MQ+;g+cMFa6z@!@Lq1S2xRSJR?kPn9Qa~v3v_*yMUR^ut-g9mM|Y6bO#6n93UL9 zU;iI{8mykCwpQ6a3gmtMNR7!+0Va8SMvf6MaZKL9$K(zfZ3AdA=^QIy0yi7xt$&1m zLHF1XM}92ivSyqP%x`8HzV!FV92P8%d?mX#5{~AbHokCrIoz{M}zfW?ubS3X_g~nYl_-Qi&+Bp$`+miS|TCWj9uB_?+bTS|QSpS${qR zOia;h_Rzbo#@SzB9=ohzO%lur&uyVE5^p_#k4Zz#bj*k%gKVqh3?o>(_p9HtvoCoB z1a~#E3qnJdYN0jhW)Gn=L^kxJc`}ht023d$Njj<$)Iss;KYc4L<5Z=(TAyY2=+RW5 zAAlilr-%Y^yYi4w+iFnjM;lVW{-)1bz+^)z9r(#(2~6a@oc}aMsNtT#v2qIMdspEs zkr?*oG3rb`B))V~9bz%5PhgZy%9;c3yuGXd^oz+ITDT+ir)5MI;q2>06+W;ocuQl?fxQCNlnalx#G`Tf{6M>Td`i&a9cs^C#sha?k3Rz*@ zdEDoFP1HSVxyIWO@81lV$lw&w;~eTGx5vKwou4%}8nXLLu+)wjrxU*jz4F~fv{hD8 zpirO6G@{~hLWd{NJk3ty1Pd@Re6)gE&<*~2ae#GChCa}6;UT`EsU`0tCqg%*(oA&o z|9me?H67K+d|1l(0F*ABjda^)=4`BvjyR*Pr1T;A%RYgwEnsp;`lGoTP2=Ub0+V93 zuj$wfx3icdB6kU^eVgxMvV<9Th;#ubF}ru;#OcB%JK6F#3wFuJuz9FzBNOS`H?qB= z+-oVLq2?8QE6J8^cJqrQsdPJwC|i}OIZtYOi}^u^+dR#KnP)9AhXmp-UI}0l3{KtkU#Lw^#SX%E z048cmL~^RGVxJEXTbVH7^I!f%gi%S0i~vWk;d@czW?(W+RL%tEmeKFCTPrjzOf7nP zcj0~Ag#+vfB6L&@22(wPk?flcWTZ4f8s%|vrHr}Lvt}YRCiaESwcQRNlBo@{X`s%Q zh44DcOu83+wJZ0`20#m#>iM9fr(8t7squ%glm{1%wk&8kB0UuxAiYO zORlMNB*H8aO_`Z>LvZ`kR>>*g;!sWW)&Kq7G|sjRO9X&q>G;r^xTIvkH-Joi^1ppG zqENR@IQk`?1rVBMn}l)FaZJ4RcB%s|)qFR!Cv=f1CVbgv)xN%PhHYn0x-$8&D!i&V zH0#J*;+Oy?n;`O<1zO2~PRtuch4bUZpNI7`111o}0lW;7n`TSs5zx202%DnU;Vpw} zXRDIEvP#I*X~G|iRK^Xv*I{kpe%7l=7}oHNpZwDm!g+t3W^qn+6D=7;2jWP!hI;N4 zTU={yX_SqaMq5qJVfWnU0bnvq7QtDBP^41k*(O!%W!K!3Un1$9W+Ir^L2%U3bAvvI z?Th{GxWU;_JO8zR{a(8J3Q-ZE27Zvo_n?yT%72cb7 za>L)u4Bd`-H|lFADV}*t&aq`k0H2f&B8E&8Vd6Zuhl%TX&YHlasUFI(!&V*u6RN*ducr|Z-1w_+rb)~Y zbrTXr!;JeU#E?n9K_-IpH1;eRy=AM&0N(=8$t0Za^ay@$uYOkR z??+HTsR38}NX+uG-D6#Ar*}(3C8e z$u)+itb%5xjJc=8B3P&AE*xk(Nmr4jsA!@=bN%C{>zr#kyw_h>0u!<%-ucCAX_}C! z8pP2|5PPqH9PJ&$moEMdnM8;BGsL*pwL9YX7nmq8Y5`1$Zlcp;S7t-MV44h=y!=2= zG&9ILZFlfo(GH5nWSQ)|qI%J^nwU0Jc!I|#2}PC>TLNeuBJS2<2Qhut0w%jf&|SPw zAzCfn*RONwwD6z*?Pc{5fN(M);OL!aL z#r!rp(i)2F=B}~9Yg3&Xb+&!Qalh|FSqV&7)!!kh+LT*_gV^S@oy*KYgynP+XU!-7 z{%fhCvkj~BR@s08bfm!Ky?^|VREOF0@)nrr3*UjQupJO_2D`8J-y;0WNp9VeGlnSr z8u?4iR}eW;nt(|=V5FbT`Yz+3IAfJdh8uObgBm>*6O0cfV)7BE?xk(zW7(}#AgDM zQ(yU1R{xz?Pz{4$KoC}aZ zQ8eFWe{h7~N1A))nG%{aL51_UdtSL8x zD57F1>-D-_6#M*S)l!>c*4WHkq)DiR8$pHWtad(ilGWt1fS*b`iUxC(Rmf~x^+j~2 zd%3B3$xJ`_le}?0wtQwB-OcuMPs_%@1Y~AowSidyfyt#7FbTq~`zBNIQpGlRe=OZ4 zq<7jxI%H;atLq5AhMEeFlSbtLOiXjU8GjH3i;+`T*<5i%UiiuQ{%qYGPLUP^HOK|pLPQrOw&AU#t0+@*QAyc{G%kq#TOkCg?^WO0pmEQSV z3z+PjhAZzt2g&FP4MT?AcVwCs%?vA>**px)E-k?br$~Bqnj8a$^=<~3nCjkG`|E75 zHjY4A(*7tzP!2UPktV#fp~j$DNwNL^%kc>PeVHd<+eO+tK-Da^!iBd@#y$CAJ}&H^ z;|xs90Il|P?ydK7V3Gy7mY96H2KrNfZpP_DbJ7APYjfJ8fQkBxD*2~+*h<92n1Vc$ z)hWvY;Ay&VW>AS<>D63|^h6Y8S6gR3=&PFf<_0r;@1Fw#= z+3z!8ngI)sp>*H`l8_C0dC^YKMXLM~(LVqZ7tX!#PXTE>mctJ|Ha(bsI`K^j4S!Rc z<*lHF$991pEnw0_++F!fCpQC=VYZN;W~brVT>vKT!(-U-HDc|@2x-&icocxiqI-GV zABkO;7f?+eWQn9js4-1S)b})LrWtVgh~4^ta-=f|TfpQXdvu*=XHq!&=hy?2-vUg~ z%4Ql9v%ac9ZBN}ScuunL86b(Hd8f9`=j)L8xE$UTFu_-&`^>4-`_T{Juk&n7(0dI` zg1)(oY4lxAbVHjLS)^*}S(qUi96pSTfRzR21k>yLT@%n8a@WlQ7tp4(vtZVZKmZ|> zTIAmP&_p{p;y*PhK5_{#qZY*lbES$&5YtsBueRPU!jehwHpB~G6&Ka|r>t3a08m=M zWKD|h;2@ovF>Fe6ja1R^rP{5#dH6Lbo7;!RgnbuI;{fYE2%FKEcwf=*)&GO@D=Q_P zUtJ&h|1fJz+^y3T%-8XaQ6+g`1^UH)PpBiy*ig;waEe_#x4n{d1h;_6fpzDG54LYC z^v`hvCfI`=@}ZR-CyC=2;jI(wZ#KsSP7J1@&%X>Xc{(Q2IFZxDk56@HwK`=5r4qyR{Aa`qMRn*-3cZc~Z zxF+^jL;GGg4gcJ1XiS2kVrBxApheMzLjV&|q<~!Wr!2!$3n-Yoi1ab^xWZ>Q8kCCj zmpOAZ_aIvZH&MSe>iAHb);Z7uChHJpHx5tZO|3sj{l?w22$I#CS|1a`y0ifgkrA}v zV`7w(jJIBA=1eI#xuV7d6Val`IOC{Hb$3X8OahpYM*kXN&NXbK2UYZyst_7iF_xc1 zL?;1;XYliprqaEkirj1k^WU{JCYvSLK3N%KASR!T!batg z#yosXE~lZFK8X2&aN+nJeD1{2Dh&!e3YfU^+c)ReMy)pSrQicE&F78iV}YyZc)93O zt1>^Si3c#koT0yCmUVVG0yN58+Rn?OjMR){rg9jCmq$IBX;hZm2%YXg<7^gMo@t8w z5(($3=gy?wXP!(w?EO}*mNOH%bt>wZ*U!6crfdvMw4GB??NC-Oi^^$*3vY(7UNglm zl~?}hJE?9S6OgP?Id-w;gi^VjPJiZO=?u=V#g361-Z$@H8SX0CWCbQmB4L@2f95ec ztWh}+fXR14%@{=3u(3NoDNPFYzlWSyv-UZZlp_LUgb2WXjM8BC^J_cFZgj-lu<%=dECRk7c}xH% z_|!k-c}%vz2pc|ohbILllW0t?hsI=lEy%FLCp`S~fNKs2U-@YV zSwt(4A+a4*tf{)s@M@WIasuT%)&eHYV*O#>TJMOwrkr#v&#)Mo#P4H-?d_*W$3k1m z(jhb%T?p|$w50>&7U?gXQUw&-&}7t^OiT)PyNf zXT3HbhvN?c6U^k3zs~cR=xc!lQEv2JB9F;e$zy^O=&t55*-awm@{Wl;0|mvs*O&_g zEN-3RmMLNG5u8j-N951!-TSFy9)2cMxf z|Jlpo!&hPtA9rJpNFIN;Mm>ruHh(@0On&~`G)>6srL--}1AdCx^w2(`ueFrNue z)Ks~}`BP?Z-%InPzA)mao%6aQ=!2qbLfjzcE@cPFLjft(N;Q?4 z-zwyLF(pRV`P13HJ^U`pCg!>az=Vrk)sMP~pZf*8{-=nESB2v+)mgLmeNU#L&weZo zkWFwUV$7b`dS6L_049Kl8l9^tr%;KzS5Cq<0rj;VW+9@|kWs|%z`_I_lM+TS_y;tx~ z@psc_4jTff80DkVzzf1Q?3}&aO{L`LPC0weD9Splr z37-1c2ML{GcVQ;UPz%cJt+Ijw(sJqwy9@sdktldZhi#85w9l1PZGkBDFKK8@R{VO6 zKZ?P_soSWB1t#@twdt>0w&v@xc3d1*nN3l>DV~_=PT@=#qPqaa$WtR8jOXc zlS2n``ivQ-Klx(F_y8p>dA)J~b;3NX$_BVf&W-(uApVM(jQ$m?+dL5U} zhOjsd$P47{Z8qqrLfz;k!yx+D0TWy3ESdfSt-=uklkT&p8zF7o=jbDj9|lw}Z<)2W z!Tb-%wm08+(De1&>0NB^HEgH?MW5XQCL54DlPWEZ$p+(a1M0^bBTt_wuRN z2=HJLYP60+NE(GBHh~@{`|JDI^X%pJG@7Ri8L;*NUbseC1x&EXM)OM@WEY&CBuuH8xAvl*DUxsz$ACw}pJ{D4SB46`VRw1Zaq@r-7x zbHU>P_T{*v_ub&$-hW?8GqdmU1EtsGwVUak|K}S4Oac^1|CYvNeah$Kxn;^(e;lq) zJY_B8pVxi@69zURpLf%B_T;KxzY`G_wOp-kK8X&(OCQO!?cjw+=<*NN3neDV-q(li|GRPihL!bIEVXo)0UE%F#)$Fa3$s=f}sbZGs z$(!H$acBWT!ws37R4XjXJ6MqSvzWa2)z3ai$IiLOrWA5J|ID-^S^LdAJ9*_+n)t)p zVI`O)3T*)&l?QVc(K^t|H2H%cem1sY>A@k^y?3IlLB|Z3c$Nnih<_O8*V{jQB`vU7 zkeTH4OpY5fsg_V_UHH2%r$Nx8TPQasnl)~oJ@qkqFFFre-2U;e)5x!0Pm8z7yjQPf z9?UX20>QlpE|3et-6|{FprL&-iaF=%KYu5}y+p5tX3KtRD|My||K$tmG<)%sv4u7? zF^^FnxdL+M)p!3Fzyy_}NzmYMfJrMVXWbDg|H30JV6wdlr;wlfr7;QFOMhuL9wo)Y zBpc67zk4l!35L^{RI4Pz(gCseL(imBFMlxdZ$$q;MzUZdez(u;FGFU>-@cZ{uoh?< zo5S%HNs6xyd*rYnFS8k9?}wjBr`Zs(4WMFqti!Hzyx`m-09nGNU~YU8t;wx)7wk|c z*kP%L^Dt&bq)N;|CaEhA4WtUruuuHOry^@?&{1Ao#g7jG6Kz5h?_5pyfAt3YFI-7; zBwA55=uqu6IaRrm1_=Qg0QvO4|56K>NLfn`O7vw|Al2{f@BJc;{Q3=SzIZjOb>b2= z8f~QP?ZO%865viB5LK;FR&`8vaMe3K>_GB0f&Yq|`7PibEn-z8LsGr~kev*i6Qo){ zL6~4aG36ZqmEewT+p&VK%-M#nZ*U|l%TJt1 zeb2v-ByKOI63({Xf78#|L{$#^B`sjGjULK{bPJelW-vZXUuA$3-M(l{njT+l?s)@0 zle?_=mytPwaO)D20p{H*JL6tLOKV2XY-d;nLeoti$FF&RnTCB7hgTzV5|K^wXryaI zh!d2(!@v0hy!cvL0HnBCppm1N5_3}*dzE!#aT$2=*)$Xx ziTyFG*rd^a08Bh1xB7wFyH0e@wSW6Q&Ma3W<6D*iFEbUm&x;Ix=BuBMq$!;sASXm_ z9slF&0nPe*$$(B)XGbAejP>}%V z=D+_4wEw5r0z2Z!y$SbovpPeE2R@%A#Orq7f}(%6@Vw2`=`dc?0w&w&m3+2pX-qah z9-FCr5WuAQnL6B$njDjez1j>WWVI#~ZTpq(?$kx9hZ8U12+O1p1v&7zE+~sgNTc<&!@d@hnL^A4t*$v5aM99nb+Dj2;A zlI$k?T@MUCgh{6xk7Z}(Ks=AZq_={_f{C*ubAUOwA;0sjpOPhSA}ylDkY=Gy#ZH;J zE94aT4B1{^j`Zle!Q9u$lQIH2U1F>2dY%x-VWoDaO6YJ2t%)hA2MryDDIS+TY5|iK zT)MeGTEJvS3H2A)5=yTi^U~_y)$xpFWMGjx1E#*rvk$q0W8D z{kgJm&Bqd$ARwii;ADY_Nkgrt-g+-BGl4des*!Z6ik8`p`%YjEH}^(`q?j#W@@P>a zjY6Z(BK`WmelPr=vZcFGkwm59XIUmg`2fJ=^v7S=955-gCPsQpky&&M71s#+=gbr3 zknJ=Kqhoq7I;V_^>nygneta3qJgiY3BXH7;_U-`?%*Z;2aHyfaGIxn8K^>?nJ?vY- zhFzyY-Y3|+E7oM&rIQngh2M|=={+3Y{+LEl;nq>#7P0qX8)aa*XO6Msg8)cFliPA< z+h(;{cC+L4-U24u=ui0yo@!}Kj*iA8dO4Q|%Iq2Z+irgQCuz={CM=ZH*g8=t56bul z_OPj6Kc?gpBnvBJl2?*ER^S9$0olm;5C(F4h80th%YIb*Hx)NHZb}Rw?&`FmC zLtERGo!gb!+Zc{V`6sb)D+-M(|NL#-LheI1NoPjl4))Ti#2!v+OinX5$)8@(JSVL* zHUTohD6vHxBZh__;M&n+b(&FwPPA zNlqg*C5wczPP}t1jr{ga^1nbQv`vNC-F@M@ItlW)d_MJK6*z~+q!Bo9+-bB+U1?qz zLxW`}c`r3vH@^GRG=GC#AQ@nhV99g*&gXZa&8i}V^epeWa5j}$2}A|C zq8X*G^_Zi2tOZQA(Q`5_{L}&_jj4J|L_A1f;$*8VSj2g=#)fP62^}^vqK-M)2pzjN zKSey)D(lIK+opL>AIj*uGYh7l(@w zW-m1ABX>zZW}L@`Mg^^Q`YA9m!R6I|`gYLT`>g~fm&3;-wq!A4C(b_@cWY}*;1UxpqWgxqb=p7e1Jmg80oFSFSG8cAg) z@hiD&=0#$-=V#`ik6IN-CP?64p_`isurn2SnxUg0SA!XjSQtRp%pjU!Dl|s zc5sKo{ZrbfCSNnz&42qrnj!s&m_Z(kEm<_>;QSgsCM2vY7WmD;_>sUQ9E9fT=`Q9R zH#eFkC%&6R1qz2es%8vLE%o;X0&)`WC04%3gP#rYW5Dc9%&h=$;#;iMgSp4we4Csl zZ>I%TCpw%7$x3F)#ir|qPebetpr1#Tu-&#!S)ZZ+*lEe@WXCFu?)~Idyz<|~&U`m5 zEKnEDTh}5M-|*yq`e%?VkW~_l*=GW!W`@{ll@81A4<#^p;%sOX?UP*rrn^mS`3b&k z0h8^G%>V{@-|m<5>k1a&*~Q$crhDR#@1>Dn{yz3I3E8Apg&&wsKek(4^r#ngQlLy?(-R<>Ryo22^>b;`?ie;$!r`rgAnL~fw-3J{v(LJ3Mne=I@qU67 zQCh&Hpu`qF9q;si3z#%U!xo5_Yb-+E?ZfWE8`P{Y>$v;t^>6$j%^?>SS;5XJqtu-6 zTTQ%bQLCga7$jA_vcRn}R`^mJ2U(EIKljd>BVUOce z+KgH(*s~<9U0vOgNq2xb$jszh2PU?J2RZqf+hhu7s{0`DS)HozWiV+Mry>N=e=0K7iTTWU*C z1@;hXHuQ-XV)7RdMA?hB+dtfV5nHKQV8{OOPMYF1gQvOqXEJa?-JrgS);j5@j=pYk z(OeEYv(Y-LFIDLl{b^lybx=m*_wTI*OtwL5_(&ny0wzZrm@tTQc`z6IMVUNIZTAuy zUKf;M)}f!>;ICY%rp|$$@PTuiksh>qu?L#$gC;hnPv=hm-V52~eRsJu<=D@@?UxwEZa--LZfh*b$7(mBuvqd&pKd?7+q%cNcpI~@;h zFnHb>&6TlkhpH5H=UD7SM(9J)Q}{Ds95VE7ft_D7sD~Oi`^bwF?CeS z56o~yeS;1(AUbciYrY5fi~d?jQ&-r;?;qJKr?H72szg+^6>OXbvR6*al(Pq&z0NIz zXE_HC-yu8l9XBO4(Gw<4cSh}E@z9NY>SZ!Mf$vx3sBqHI->TsIb#A@h;Q;kKS2Nv# zm~x7WQ3vIb8&@u3h97^6lmfs1W13|=#6*ECGmU`@6@5;ucNL$L{%0r-OS?yID;RFvcOgR0t;${b}XtRT8ZoQ z!K)IjWV-$n_~1EjRWNfm%1+uUAM*Q1TFQj&zFl>klPj3@rEx?_Dp$S6kH$muFFSem z>U-_>-+9}-ZKf?*iaBze1^XC#HaSlk#hY!9pc$tV*M_bLn7R1ZUyR%^YNevltJ?W! zV3IecP`8NdhRG}_IGG`{TOG4b_Ha8Oo18TnEVBA8LHr@qV*RYlU6psRS}*(=vWYb3 z_*IqKZTfBceiC|(qH44@hBFUf%mF@s6^AmTUlej)PyHRhU4#Wkn)#u=bm7aN!hZH# zcDMa%Ur~RaHwsg@@-?rD4$K0~%sU^Um337xN|`=cGG5vNR9 z76Yw8u7f#Lw4JBRTM8weuipFrz7;!%>WL2`YXOsudM6m8r7>|lZ>PkB2n`P$nB-%? zfo@%Nj@Rep)^~oIYWP(wqgkyjXN#2#!Qc}IV6S3+E?qp6dhtCu!ImQx@>F#I;w6Wy zq3pLbR`uJYQ`}@X-m*%?okB+Cpfbik6XHDPtFbrUNfSg%PNCtRM`ID%W9uFNHQ_K% zAp@0vr%2h~eHMQk`~qWsitDz=3q|vsJ0zeWG4_yQ!Dx0~=R`FIeSpCZ0}(VE6+hy$ z@qCXRG@YC36<>3@H|l8pIJN)?p1pN9-Gt9(&7*_1WL`@GKWe9M`by+JV}7ZSGQ)gc zZYbH@Z_SU{G%(3{(n&4L>gFCgCic>PStA;yMrK$mAaOyHNt_X*wnCwPl?Q4 zn}*=1MKdPPoXIMn!7^>E+D4e%5Ht6v*>|R*VYW@}j$zE>IKa8n;~Zuk`_6RBI>o{f z!7Skj$I7S^Q&ZTjxM_cuVia%~$=XI2=`J zu1|9%qHfEvN_yviej@`W2nNJ7z=ULXEse=4s^))6T5)aTtI} z)Gx+?Lml%C(Sn+w$5~kEhsLHbXpb_pj86k5bG9)+f-KN{fBxK7$t$go%k2G8oRNQ*M#!kHX}`q0 z*l`j~jdLRMb_JdNUGe;);Z&gc@QV5r*RD9YgFl*{`Osa8XK{`6ToRDrLf}GBcaNDu zGiRu}e1+WYWPa3LV1U4jlkC(S+IjaO^5!z%sb2=fwTUkf1k1pT6$2s7f+}}E7E~GXES9zTnxJH`Wf-I~05~~` zpAZA3n;ZuY78$Cu2V1m#rCG`%z5EfSAn}Sn;#__~oYwg2bgWTfn4XtPQF4(8)swOsJvqIK~N|F0@>z==?c< z3+K;CQ1WG~T@WdMt^ zww;B&X&nTNMahR~@#lljpqGC}Z#+1@)o(?+3u3SF`-n(Y`vgB31X2h183{TK9+)9* zg~z?d`9-$@{TfT`)l>!Oo**xbX*0sH7+|;_t+xhDib@uh=Aq+nU&Gn*Z6bF5klL8g zoe&}kTa0bq_;2O3Wx8AAvWT>woElV8L~dwtpeNCIL6qV-t3P>e47An9*ZbSY+xShC z*c1=u)*cx|W>VA1cbV+BoiN4>>54O_F>JCDoO_yT-l9)U7d|y-KSO3$KuJ8aiAHhJ zKEGvj5}>q$W{1NG&VDip+X8+vf~y4+!G++8&I;h_UIv{GT-vl}H9*?Zk&!IvZht$tP8IzTHoqEn=z6J;c5PHOO*Qw6>hjSTN029NM z?-I%BTw=Wc;!a4i3&t?D+z|{bq>-6#VfJxC1i0))y7}m5CIurRU zSx8M^zhFo)=N%L>nJ=et)nx4;s4AYh9jwBm(yYq;)Gu8%`UnIu2}n064Q#LCob zbYI@lMHL?PY3I9=4qG2YT4a)P<0ccKErGN})U!AwsSR1AEs24<$T?M?vlA2Ht02%Q z_6hQNnr%Ym2Z7Zp^uSELggrTFH$2+cA^4-}yKCoTG-gB6d@J^o8k1?&ZGg!sY=KK0 zTh>900jx8uRaPLyKiVv+)sZ(c#R5_~5kv|kS510Whbj^pbP{6c-?1gQYB}$bzqZpJ zdB%avl8Dap4jG@>CxQ%%KyrZ<(jqTEYxHTNKbJXf^nva2+Uc?J(4y#^EeASK%G%9O z4Um@mW9cm01Qd4Q5QcI0O}@#Cw6hSB*VwS?F4;DhS)uu~42*ZA6QBHW8sw$Q)|Fa} z3F>j^O5UCpFo9XNQJMk3fjYmgRrkDaW3|h;sG$X(LKaN^naDy+zHVm`VrWA z+aze|W<$^lV_fa&GUg9uK*dc$F-3GDr}ZB!kx{lpv|pHf3K;;O^L6WmAL%Dn$nANw zI}6AwWm%m`P|ZkvbTTa=ua{=fitt%iuDe>-gh0JRj__&3jbl+6ZszUYC(ft7=Prk9 zfl;L9axx+@%C;)ILGz?Ly`qc zv7!%3oQrvp_UXg1JwiwIR=fq>a{0TUKN9W?(q0}}7@thDCKBa)n~OpErKSufiJ!l- z4nnTTZ$<`PP#}RI06=_Ab>(n=42M^rxB;zq3{1Sgn@7!4$tlboGw*>~uc2)i#RNj- zWfJRarJeq=j==1bVp1T#XI-u6U%PnT zb27CPn4ak(XbGXp&xvdqW{&VV7+L#R9Y-Cn>!3s8xBOnmUh7y)vc>V9fr0Rf{-wd9F2?q&{kmNq@bHEkRT^&txlh?^!0jyr8PBmvk2APDj*DKmH_c zLK|7ieg0m_Yk7iF0F#lQzZRw(wF=E9v>1C_8j&kabau{@&h5;JCF)xlB1tDQS2Isc z^0*7c{OBjg?H9{dIJls@zZ3X5X9`lSvx~txiU;S+*c1|wGB-(d8*pusz8UP7MuW_-GGAtu zE-09%k#$spPAZICP-31lgWuRdCqJ2_LS%~_Wn{@Od>DQipwtEt-K5E1>kze+6IS)wq7$3H4gxP_jyu#<^M*@^WhUL8XP?S4 zsNyiCwjg5C3u60u*oys=7yHQ2wXipn>ty)VKSbDb8v|aj=tM4)1vqJL3b#=0C>=6W zPu4f~lf8<)i%B2G1e}l{A(imB*2EQ}?7`!UYOM1~U(7Ft;L&k61U**<;Nv;MoNoAI zf%<0=M)se5Mwl9AsRB^Ri`hRPlZ8kNNQyzzrl#xpr_&HQ3mh;lV3K!-d;;xSz@(Y+ zbJ}d;Blr~>9(rJs_j4u-76Bu3m^`nfkzc(|)DBMatd8}=2w8wki~?VTOyzICxnK!@ z6s}WlD=+JgL&lpErLxlChiTgqPiO1nY4sPL^-AAy`3@N!D%-o)ns7` z`I_K`XWEX!d>-=)=bhkvEg^u)f)O;@M&5Mm!vT1T>v>xI*0x3aHdo6nT_vq41uz*W z57^j`f0JrNbh_}h{yy6Ux<$EVu^kWS4CsKK(%Hp_C<^U_sX1`AN5pgVm`y~Ae*9>{9S@AKxMP0)IGfJyN{K3#c-`Ufq(J>!)7AG2wZPm&A?YB1ITR(FwN0lTcb$UKl7@Z zXV7Z#{6cW}smfEqMAay^!=_~Y=GbwVWX^4T^PCUIc}#7aS{}_%?txLZA3^du_L;vz zdhDd{8m63o`+m9?rW|RK32!qZ58_;RBuqKf*)l>p0G)=H#!;WMG7_k21@CZ0hTyyW z_g}$@?b+<&Bk+Fv$K)}=ry?dg7D+}5bYa);zKjWWkMo!evzguu+GA-bj|u*}(IWI= z+UqA9bU(s7fU%&WaGP4WN)Wr6%0@<2s>HUI;> zC7#^To8ZqX1(Ki{7kxr zOyaxXs0+Kg4VlaJRx#w68EN?*HRhlT>F`j=X|K%fV8PajiMo?9`k=%w9{joPx19ET z%+CP?(V9sAqDQ2AL0jt=PRE>eo(!w{*U8ir;iEs3$RrT z)ZM7SF5hDqZRhoW{~=C<*;uWi*3~yhHSrQOHC?x!^+%=L29(?ZAaqRPXT?fY4PQ44 z6P*mZUU+{RV$Tj28OneF2l?38YWwV0JHDYY!SUMF@vN$F#+gxXY8krlA?SXY{7wRs zNXae#!q*1@Oo~R*W+zYm36$5Gd1mlKo5yU@1|R6V;e-{7Jk8>?Gc+aH$c!-8}xyWL~Byqj;Z$#Um zxiu^}e#rIK(E9Cb;kltTLC!|R5GHgTMwc*0>-VUyqrNg`Np()F^RkXH@#k{XIV^b0 z7TAtxNN~=MWo|)>V!l)cTMD`B++5I1VB!f);*n5QpFIFk8Sqzu?r!93RA(&uiW^(m zEzo@TfJspSKZ9a?_!ac4mP9Rz(IGQ8??s$>tkyy3=0J%!XP8U%p@cD;Y_{fna8vJ< zb-{yUe#?$$1Z@6ZoXa{g`^)der0M;`-+*UYe5M#t?`(Z11R*lElZi>k0%bFiWh%hD zHWGRZ#Eu6Ymbbi*tWF3NCsVau07o@k_S4jr8zG#Gzwe-5f?m*I(G?AhThP`46IzvE z<7~i(V9MK?QLE^kW&%7cvb-up2VeB)kHP5x<+xXScg=nR9|gJU+*plrTr;1M0&YmA zVA8X_=MrE5fa$8uz6;^+C)e+|@AARc04D$P-8Ayb>){-2D;&&S0NAdNK9{;N8@a{& zp#~;-dxqH)>)OA3FZIY1nAT=+Y%_hi6YDG;@xK52uhU7|bEr$f-|)_@@BJd(`~EA@ z9tFO8#`KYjy##M`V$wSmnO^}W{ATxn3Ar@LeKh;C*8-T-oXjZ|wFqdPItiQX1(*yF z#dlyDlcEE2S{372fM%Us%_|G0*`atEzri`=_qY z8#2IgT0Do(2eP0z>XDUlpt#0rc~v1|y^}J?@ox!}0?*RHQtfBolSU4m&orTtRW*0W zkE?krZd800$M^9wXoLLpvv5S#$H(nM+*D94N*x;4Cnlp-%DE~a+P+c78ci9khc?bx z%hES*&*o%q(b$zVFOC*GP>@La2i*&q%Zb>E2A2j+^%Ve!8WIzzI!~I>wve+$z2e@* z4T}do;&X3+N%KPsrl%tWC*kiAD>2mdAVvX-30js6H3pUIgj(nP0@@TEpW5ie4w;>M zX179k$gTxnH$SWC`-*Eh?&K*!jfya?dKp4%7K)vb6%;0cGvSTi&aAGT+=^T)kI`5dQ z$E4Y6qCHHQY%nOu>|=Y{?rjV3gA{t;M9T$6{!~%Z+9%qw+wff1XGF76fD*mF)taC# z@3bGaCkslln?HE?ChGOz;*Gxr&EVDhzj!TnbZvLBk=IqzlcD6@FQq6-Z1=gl z(Gf-6o9-39XlhBq!5lw5#=JS6iU5Lz5%L4kE=?}V1l9lM5%O;*)B-@nXnvF!Q1(dw zTz)98<0sH$v4e#P{UxX`u{w`&PDD!L?{+kJ`c;I9mWk5nz$~gW3zTSTPWJU6X>1J& zI#Um2UCd&`KxgLSxlzO4apwvQ zNDww7pyvf45>(rxwy>Bu5WuDgHln1tgqqg_@etpRPYEy(114wACWZ>fWd{hWrv(O! zIRj?yk^z^#0OD+AB*2JbU(#>(m7vi{zQhJ(CBy5`bQwOUAH=#P+L)hhYU8@sZ}LP47$z4ZowU!p z5)x3^2HT}4y*^4=cpvK>>#>W^-()p6eVLQ$G%Gm0^IaUMf|l0dE5K*pljK-n%pB%6 zG&fcsDqkjm)VE3zPR|5rRXeK6IW$?E*^E$czV}1l^S)KKnN!Csq{=BLARWZLe+QrX ztHoX^@V5LD66G-PMDrN2aZPoKRX**A^aCtbB7qq^qVL2UG9l70@OO<>zmBT0GDQ25aV9DFs_x!nfy@ml9@uK*%D(~t*5U{qfJC0+R=yx?SL#DRd;u~yBPzvVXyVQr^|zd138xEq=TZz|2{OQck+jv}d>^^w^<7l6|N!GG>k zTp#_!`QV}AKK^E(N(fyiprVoabECf6+HKPjDAC9CWn-K+w+DLKS3&n9&=#v06Jqp> z4S7)Pk8J!m3D*awcl50x(|B%tZ>FKsbO6^3yzb~hd->x+1~~S8j$@CHILuhqkI+)^ zizgNVvgWl?vtk5f(RQzK#Xk?|brU;%N4%R8pQ~@>b(!$eZ18wj`vwq)3^(_Pi^wec z5t=y}Dz~kVW6Donjne$Raqy6Sk+wn5tbRp1GP*e*7IR|uS;#iQp}tEl3IphQ5C79A zk!X^0hFBLex4HF>-(Jkz=ACg)Y%bE%#k#;*>q0Tk8{?#D%y^7i@la+6X}*he-(O%N z1)4gcnIQ39?r-8RJICB2hK#K)9yg_OKx?k^T8SgkBlijDx=5MqJv}EO19`IwkJ40^ zRgYfzxLqXp${QcjznbW>x`v+0m@q%X0fmtou42#>zllMmYFG5{0177Nto^}1g-r)e zYo=W;20-y!ncU~70Wqo5io!FCDn{3=?w{hMKU#fm#f^6PkEVJx-+9eCHeYZUze~gH zf1`E{QZ~LcP95$q1wZ8N2@#Xa84hLqycaFVpIbcMN92#qz8nQQHdj6x&}czSnkx>` zo@_CKhSk-26BtxJXE~)vhNdjwZ)JcJp^oZq7N|Us=c4!+?x#FA2qAp@T!iHHi4U%6 z)J9t7=k|5X=hfZOM^SHYvYID2x1z22`Jgl4Pwi~T-TWTPT+Ocn!xb{Ose;N{&0&%E zM4C1iX$~NXTwb;*gF8YXP769;}0kn_x$+R_@1ZxbO!_a5JOIf{|$)Nxa33?dn*j%yo zzT4lnWsUQV@o`kzVjI?IPqf2Rw4=d8YqWPqhk_rx!vjiY&pl%Nuoe-5MQoR^3}CEv z*{&apzVxXXO&fi8l=yM8M6s%Rvt?;p(%9+<38itJ1iK~`T{2$$8z z^KM*yI1@m}k%1p-={_IWppUG{di~_N_4+D)SM*i!>A_ZMQJ_VE!%cyFB5h2lO|@N1 z7HlK~TmZ#yS~8%Ciuu;t{r!LmzU_J+_8;zMA2qkN z)nCojXnk){;Al`_H|0S~2CU!byGiHfryMZNj|MPlCRpoxivleQv?$P`K#Kw`3LIMs zw1CO6?b=quS`=tephbZe1zHq1dK72@lcU#Xts1u|(4s(#0xb%(C~#~k&;lmMwrg7r zYf+#@fffZ?6lhW4=uw~rOpackwQAg=K#Kw`3bZKDqQJ4G!2b_v^Gn-lX=^zE0000< KMNUMnLSTa2(*+s; literal 0 HcmV?d00001 From 630e41e4198a34ba39baa3fc744294dde1bad96d Mon Sep 17 00:00:00 2001 From: Abdelkrim Dib Date: Wed, 11 Jan 2023 14:13:47 +0100 Subject: [PATCH 15/27] fix koios --- .../module/src/providers/koios.provider.ts | 48 +++++++++---------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/packages/module/src/providers/koios.provider.ts b/packages/module/src/providers/koios.provider.ts index c4247241..40df5ba3 100644 --- a/packages/module/src/providers/koios.provider.ts +++ b/packages/module/src/providers/koios.provider.ts @@ -151,21 +151,21 @@ export class KoiosProvider implements IFetcher, IListener, ISubmitter { if (status === 200) return { - confirmations: data.num_confirmations, - epoch: data.epoch_no, - epochSlot: data.epoch_slot.toString(), - fees: data.total_fees ?? '', - hash: data.hash, - nextBlock: data.child_hash ?? '', - operationalCertificate: data.op_cert, - output: data.total_output ?? '0', - previousBlock: data.parent_hash, - size: data.block_size, - slot: data.abs_slot.toString(), - slotLeader: data.pool ?? '', - time: data.block_time, - txCount: data.tx_count, - VRFKey: data.vrf_key, + 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); @@ -234,15 +234,15 @@ export class KoiosProvider implements IFetcher, IListener, ISubmitter { if (status === 200) return { - block: data.block_hash, - deposit: data.deposit, - fees: data.fee, - hash: data.tx_hash, - index: data.tx_block_index, - invalidAfter: data.invalid_after?.toString() ?? '', - invalidBefore: data.invalid_before?.toString() ?? '', - slot: data.absolute_slot.toString(), - size: data.tx_size, + 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); From c710bd97e3b51cf271cdffb6cbe7b1aafbecd021 Mon Sep 17 00:00:00 2001 From: Abdelkrim Dib Date: Thu, 12 Jan 2023 12:10:01 +0100 Subject: [PATCH 16/27] implement evaluateTx --- .../module/src/providers/ogmios.provider.ts | 60 +++++++++++-------- .../module/src/providers/tango.provider.ts | 28 ++++++++- 2 files changed, 60 insertions(+), 28 deletions(-) diff --git a/packages/module/src/providers/ogmios.provider.ts b/packages/module/src/providers/ogmios.provider.ts index ba3aa8d7..89242d9b 100644 --- a/packages/module/src/providers/ogmios.provider.ts +++ b/packages/module/src/providers/ogmios.provider.ts @@ -6,28 +6,6 @@ export class OgmiosProvider implements IEvaluator, ISubmitter { private readonly _ogmiosURL = 'wss://ogmios-api.mainnet.dandelion.link', ) {} - 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 evaluateTx(tx: string): Promise[]> { const client = await this.open(); @@ -41,9 +19,19 @@ export class OgmiosProvider implements IEvaluator, ISubmitter { const { result } = JSON.parse(response.data); if (result.EvaluationResult) { - resolve(result.EvaluationResult); - } - else { + 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].cpu, + }, + } + ) + ); + } else { reject(result.EvaluationFailure); } @@ -55,6 +43,28 @@ export class OgmiosProvider implements IEvaluator, ISubmitter { }); } + 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(); diff --git a/packages/module/src/providers/tango.provider.ts b/packages/module/src/providers/tango.provider.ts index 83e7b6d3..70beecf1 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, IListener, 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, BlockInfo, + AccountInfo, Action, Asset, AssetMetadata, BlockInfo, PlutusScript, Protocol, TransactionInfo, UTxO, } from '@mesh/common/types'; -export class TangoProvider implements IFetcher, IListener, ISubmitter { +export class TangoProvider implements IEvaluator, IFetcher, IListener, ISubmitter { private readonly _axiosInstance: AxiosInstance; constructor( @@ -25,6 +25,28 @@ export class TangoProvider implements IFetcher, IListener, 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') From 4ed9dcdedd877d8e2284639b9053d75c7949168e Mon Sep 17 00:00:00 2001 From: Abdelkrim Dib Date: Sat, 21 Jan 2023 16:34:45 +0100 Subject: [PATCH 17/27] quick fixes and refactoring --- packages/demo/pages/providers/ogmios.tsx | 2 +- packages/module/src/common/constants.ts | 7 +++++++ packages/module/src/common/helpers/index.ts | 2 ++ .../src/common/helpers/readTransaction.ts | 6 ++++++ packages/module/src/common/types/Network.ts | 12 ++++++----- .../src/providers/blockfrost.provider.ts | 20 ++++++++++++------ .../module/src/providers/ogmios.provider.ts | 21 ++++++++++++------- .../module/src/providers/tango.provider.ts | 2 +- 8 files changed, 52 insertions(+), 20 deletions(-) create mode 100644 packages/module/src/common/helpers/readTransaction.ts diff --git a/packages/demo/pages/providers/ogmios.tsx b/packages/demo/pages/providers/ogmios.tsx index 035309f2..68569c13 100644 --- a/packages/demo/pages/providers/ogmios.tsx +++ b/packages/demo/pages/providers/ogmios.tsx @@ -108,7 +108,7 @@ function Main({ network }) { useEffect(() => { async function load() { - const _provider = new OgmiosProvider(); + const _provider = new OgmiosProvider('preprod'); setProvider(_provider); } load(); diff --git a/packages/module/src/common/constants.ts b/packages/module/src/common/constants.ts index b7730ab4..4cefe929 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: 'https://ogmios-api.mainnet.dandelion.link', + preprod: 'https://ogmios-api.iohk-preprod.dandelion.link', + preview: '__TBD_SOON_TM__', + testnet: 'https://ogmios-api.testnet.dandelion.link', +}; + export const SUPPORTED_WALLETS = [ 'begin', 'eternl', 'flint', 'nami', 'nufi', 'gerowallet', 'typhoncip30', ]; diff --git a/packages/module/src/common/helpers/index.ts b/packages/module/src/common/helpers/index.ts index 1711e50e..c71746fe 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 00000000..71a1e00f --- /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/Network.ts b/packages/module/src/common/types/Network.ts index 2660efa8..7b8b7ebb 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/providers/blockfrost.provider.ts b/packages/module/src/providers/blockfrost.provider.ts index a2396301..78aee4a3 100644 --- a/packages/module/src/providers/blockfrost.provider.ts +++ b/packages/module/src/providers/blockfrost.provider.ts @@ -13,12 +13,20 @@ import type { 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 (args.length === 1 && 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 { diff --git a/packages/module/src/providers/ogmios.provider.ts b/packages/module/src/providers/ogmios.provider.ts index 89242d9b..23f558a3 100644 --- a/packages/module/src/providers/ogmios.provider.ts +++ b/packages/module/src/providers/ogmios.provider.ts @@ -1,10 +1,18 @@ +import { SUPPORTED_OGMIOS_LINKS } from '@mesh/common/constants'; import { IEvaluator, ISubmitter } from '@mesh/common/contracts'; -import type { Action } from '@mesh/common/types'; +import { Action, isNetwork, Network } from '@mesh/common/types'; export class OgmiosProvider implements IEvaluator, ISubmitter { - constructor( - private readonly _ogmiosURL = 'wss://ogmios-api.mainnet.dandelion.link', - ) {} + private readonly _baseUrl: string; + + constructor(baseUrl: string); + constructor(network: Network); + + constructor(args: string | Network) { + this._baseUrl = isNetwork(args) + ? SUPPORTED_OGMIOS_LINKS[args] + : args; + } async evaluateTx(tx: string): Promise[]> { const client = await this.open(); @@ -79,8 +87,7 @@ export class OgmiosProvider implements IEvaluator, ISubmitter { if (result.SubmitSuccess) { resolve(result.SubmitSuccess.txId); - } - else { + } else { reject(result.SubmitFail); } @@ -93,7 +100,7 @@ export class OgmiosProvider implements IEvaluator, ISubmitter { } private async open(): Promise { - const client = new WebSocket(this._ogmiosURL); + const client = new WebSocket(this._baseUrl); await new Promise((resolve) => { client.addEventListener('open', () => resolve(true), { once: true }); diff --git a/packages/module/src/providers/tango.provider.ts b/packages/module/src/providers/tango.provider.ts index 70beecf1..b83227e5 100644 --- a/packages/module/src/providers/tango.provider.ts +++ b/packages/module/src/providers/tango.provider.ts @@ -179,7 +179,7 @@ export class TangoProvider implements IEvaluator, IFetcher, IListener, ISubmitte 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); From 694bdc37d646138817f7a230b77ecb3a1dffcdc1 Mon Sep 17 00:00:00 2001 From: Abdelkrim Dib Date: Sat, 21 Jan 2023 16:45:30 +0100 Subject: [PATCH 18/27] remove unnecessary check --- packages/module/src/providers/blockfrost.provider.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/module/src/providers/blockfrost.provider.ts b/packages/module/src/providers/blockfrost.provider.ts index 78aee4a3..f0032ff4 100644 --- a/packages/module/src/providers/blockfrost.provider.ts +++ b/packages/module/src/providers/blockfrost.provider.ts @@ -17,7 +17,7 @@ export class BlockfrostProvider implements IFetcher, IListener, ISubmitter { constructor(projectId: string, version?: number); constructor(...args: unknown[]) { - if (args.length === 1 && typeof args[0] === 'string' && args[0].startsWith('http')) { + if (typeof args[0] === 'string' && args[0].startsWith('http')) { this._axiosInstance = axios.create({ baseURL: args[0] }); } else { const projectId = args[0] as string; From d2e52919599715435986407a51b651780ae184d9 Mon Sep 17 00:00:00 2001 From: Abdelkrim Dib Date: Sun, 22 Jan 2023 15:12:00 +0100 Subject: [PATCH 19/27] fix ogmios url --- packages/module/src/common/constants.ts | 6 +++--- packages/module/src/providers/ogmios.provider.ts | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/module/src/common/constants.ts b/packages/module/src/common/constants.ts index 4cefe929..93e50ce5 100644 --- a/packages/module/src/common/constants.ts +++ b/packages/module/src/common/constants.ts @@ -81,10 +81,10 @@ export const SUPPORTED_HANDLES: Record = { }; export const SUPPORTED_OGMIOS_LINKS: Record = { - mainnet: 'https://ogmios-api.mainnet.dandelion.link', - preprod: 'https://ogmios-api.iohk-preprod.dandelion.link', + mainnet: 'wss://ogmios-api.mainnet.dandelion.link', + preprod: 'wss://ogmios-api.iohk-preprod.dandelion.link', preview: '__TBD_SOON_TM__', - testnet: 'https://ogmios-api.testnet.dandelion.link', + testnet: 'wss://ogmios-api.testnet.dandelion.link', }; export const SUPPORTED_WALLETS = [ diff --git a/packages/module/src/providers/ogmios.provider.ts b/packages/module/src/providers/ogmios.provider.ts index 23f558a3..2239731b 100644 --- a/packages/module/src/providers/ogmios.provider.ts +++ b/packages/module/src/providers/ogmios.provider.ts @@ -8,10 +8,10 @@ export class OgmiosProvider implements IEvaluator, ISubmitter { constructor(baseUrl: string); constructor(network: Network); - constructor(args: string | Network) { - this._baseUrl = isNetwork(args) - ? SUPPORTED_OGMIOS_LINKS[args] - : args; + constructor(...args: unknown[]) { + this._baseUrl = isNetwork(args[0]) + ? SUPPORTED_OGMIOS_LINKS[args[0]] + : args[0] as string; } async evaluateTx(tx: string): Promise[]> { From 8ea5e1cbd860e8e38d4fdf9883c939ad5dbb8987 Mon Sep 17 00:00:00 2001 From: "Hong Jing (Jingles)" Date: Tue, 24 Jan 2023 14:45:04 +0800 Subject: [PATCH 20/27] add docs for providers --- packages/cli/src/index.ts | 2 +- .../components/common/blockchainProvider.tsx | 2 +- .../components/pages/providers/evaluator.tsx | 51 ++++++++ .../pages/providers/evaluator/evaluateTx.tsx | 92 +++++++++++++++ .../components/pages/providers/listener.tsx | 1 + .../pages/providers/listener/onNextTx.tsx | 87 ++++++++++++++ .../providers/listener/onTxConfirmed.tsx | 18 ++- packages/demo/pages/providers/blockfrost.tsx | 10 +- packages/demo/pages/providers/koios.tsx | 12 +- packages/demo/pages/providers/ogmios.tsx | 111 +++++++++++------- 10 files changed, 335 insertions(+), 51 deletions(-) create mode 100644 packages/demo/components/pages/providers/evaluator.tsx create mode 100644 packages/demo/components/pages/providers/evaluator/evaluateTx.tsx create mode 100644 packages/demo/components/pages/providers/listener/onNextTx.tsx diff --git a/packages/cli/src/index.ts b/packages/cli/src/index.ts index da674518..8417a347 100644 --- a/packages/cli/src/index.ts +++ b/packages/cli/src/index.ts @@ -47,7 +47,7 @@ const main = async () => { createOption( '-t, --template ', `The template to start your project from.` - ).choices(['starter', 'minting', 'staking', 'marketplace']) + ).choices(['starter', 'minting', 'staking', 'marketplace', 'signin']) ) .addOption( createOption( diff --git a/packages/demo/components/common/blockchainProvider.tsx b/packages/demo/components/common/blockchainProvider.tsx index cb322c82..55af4a59 100644 --- a/packages/demo/components/common/blockchainProvider.tsx +++ b/packages/demo/components/common/blockchainProvider.tsx @@ -9,7 +9,7 @@ export default function BlockchainProviderCodeSnippet() { codeBF += `const blockchainProvider = new BlockfrostProvider('');`; let codeKoios = `import { KoiosProvider } from '@meshsdk/core';\n\n`; - codeKoios += `const blockchainProvider = new KoiosProvider('');`; + codeKoios += `const blockchainProvider = new KoiosProvider('<'api'|'preview'|'preprod'|'guild'>');`; let codeTango = `import { TangoProvider } from '@meshsdk/core';\n\n`; codeTango += `const blockchainProvider = new TangoProvider(\n`; diff --git a/packages/demo/components/pages/providers/evaluator.tsx b/packages/demo/components/pages/providers/evaluator.tsx new file mode 100644 index 00000000..12b89c65 --- /dev/null +++ b/packages/demo/components/pages/providers/evaluator.tsx @@ -0,0 +1,51 @@ +import { useEffect, useState } from 'react'; +import SectionTwoCol from '../../common/sectionTwoCol'; +import { demoAddresses } from '../../../configs/demo'; +import { BadgeEvaluator } from './badges'; +import { + onTxConfirmedLeft, + onTxConfirmedRight, +} from './listener/onTxConfirmed'; +import { useWallet } from '@meshsdk/react'; + +export default function Evaluator({ evaluator, evaluatorName }) { + const { wallet, connected } = useWallet(); + const [address, setAddress] = useState(demoAddresses.testnet); + const [lovelace, setLovelace] = useState('5000000'); + + useEffect(() => { + async function init() { + setAddress( + (await wallet.getNetworkId()) === 1 + ? demoAddresses.mainnet + : demoAddresses.testnet + ); + } + if (connected) { + init(); + } + }, [connected]); + + return ( + <> + {/* } + /> */} + + ); +} diff --git a/packages/demo/components/pages/providers/evaluator/evaluateTx.tsx b/packages/demo/components/pages/providers/evaluator/evaluateTx.tsx new file mode 100644 index 00000000..c42a11d8 --- /dev/null +++ b/packages/demo/components/pages/providers/evaluator/evaluateTx.tsx @@ -0,0 +1,92 @@ +import { useState } from 'react'; +import RunDemoButton from '../../../common/runDemoButton'; +import RunDemoResult from '../../../common/runDemoResult'; +import Card from '../../../ui/card'; +import Codeblock from '../../../ui/codeblock'; +import Input from '../../../ui/input'; +import { Transaction } from '@meshsdk/core'; +import { useWallet, CardanoWallet } from '@meshsdk/react'; +import { ArrowPathIcon, CheckIcon } from '@heroicons/react/24/solid'; + +export function evaluateTxLeft({ listenerName, txHash }) { + let code1 = `const tx = new Transaction({ initiator: wallet });\n`; + // code1 += `tx.sendLovelace('${address}', '${lovelace}');\n`; + // code1 += `\n`; + // code1 += `const unsignedTx = await tx.build();\n`; + // code1 += `const signedTx = await wallet.signTx(unsignedTx);\n`; + // code1 += `const txHash = await wallet.submitTx(signedTx);\n`; + // code1 += `\n`; + code1 += `${listenerName}.onTxConfirmed(txHash, () => {\n`; + code1 += ` console.log('Transaction confirmed');\n`; + code1 += `});\n`; + + return ( + <> +

+ Allow you to listen to a transaction confirmation. Upon confirmation, + the callback will be called. +

+ + + ); +} + +export function evaluateTxRight({ + listener, + txHash, + setTxHash, +}) { + const { wallet, connected } = useWallet(); + + const [loading, setLoading] = useState(false); + const [response, setResponse] = useState(null); + const [responseError, setResponseError] = useState(null); + const [txWaitingConfrim, setTxWaitingConfrim] = + useState(null); + + async function runDemo() { + setLoading(true); + setResponse(null); + setResponseError(null); + setTxWaitingConfrim(null); + + try { + const tx = new Transaction({ initiator: wallet }); + // tx.sendLovelace(address, lovelace); + + const unsignedTx = await tx.build(); + const signedTx = await wallet.signTx(unsignedTx); + const txHash = await wallet.submitTx(signedTx); + + setResponse(txHash); + + setTxWaitingConfrim(false); + listener.onTxConfirmed(txHash, () => { + setTxWaitingConfrim(true); + }); + } catch (error) { + setResponseError(`${error}`); + } + setLoading(false); + } + + return ( + <> + + + {connected ? ( + + ) : ( + + )} + + + + + + ); +} diff --git a/packages/demo/components/pages/providers/listener.tsx b/packages/demo/components/pages/providers/listener.tsx index a415eb74..19948365 100644 --- a/packages/demo/components/pages/providers/listener.tsx +++ b/packages/demo/components/pages/providers/listener.tsx @@ -6,6 +6,7 @@ import { onTxConfirmedLeft, onTxConfirmedRight, } from './listener/onTxConfirmed'; +import { onNextTxLeft, onNextTxRight } from './listener/onNextTx'; import { useWallet } from '@meshsdk/react'; export default function Listener({ listener, listenerName }) { diff --git a/packages/demo/components/pages/providers/listener/onNextTx.tsx b/packages/demo/components/pages/providers/listener/onNextTx.tsx new file mode 100644 index 00000000..858ff2e1 --- /dev/null +++ b/packages/demo/components/pages/providers/listener/onNextTx.tsx @@ -0,0 +1,87 @@ +import { useEffect, useState } from 'react'; +import RunDemoButton from '../../../common/runDemoButton'; +import RunDemoResult from '../../../common/runDemoResult'; +import Card from '../../../ui/card'; +import Codeblock from '../../../ui/codeblock'; +import { Transaction } from '@meshsdk/core'; +import { useWallet, CardanoWallet } from '@meshsdk/react'; +import { demoAddresses } from '../../../../configs/demo'; + +export function onNextTxLeft({ listenerName }) { + let code1 = ``; + code1 += `${listenerName}.onNextTx((tx) => {\n`; + code1 += ` console.log('onNextTx', tx);\n`; + code1 += `});\n`; + + return ( + <> +

Listen for transactions that are submitted to the network.

+ + + ); +} + +export function onNextTxRight({ listener }) { + const { wallet, connected } = useWallet(); + + const [loading, setLoading] = useState(false); + const [response, setResponse] = useState(null); + const [responseError, setResponseError] = useState(null); + const [onNextTxLogs, setonNextTxLogs] = useState(''); + + async function runDemo() { + setLoading(true); + setResponse(null); + setResponseError(null); + + try { + const tx = new Transaction({ initiator: wallet }); + tx.sendLovelace(demoAddresses.testnet, '2000000'); + + const unsignedTx = await tx.build(); + const signedTx = await wallet.signTx(unsignedTx); + const txHash = await listener.submitTx(signedTx); + console.log('txHash', txHash); + + // setResponse(txHash); + } catch (error) { + setResponseError(`${error}`); + } + setLoading(false); + } + + useEffect(() => { + if (connected) { + listener.onNextTx((tx) => { + console.log(111, 'ogmiosProvider.onNextTx', tx); + const tmp = onNextTxLogs + `${tx}\n`; + setonNextTxLogs(tmp); + }); + } + }, [connected]); + + return ( + <> + + {connected ? ( + <> + +
onNextTx Logs
+ + + ) : ( + + )} + + {/* */} + + +
+ + ); +} diff --git a/packages/demo/components/pages/providers/listener/onTxConfirmed.tsx b/packages/demo/components/pages/providers/listener/onTxConfirmed.tsx index 7b10e2a5..21ed22e9 100644 --- a/packages/demo/components/pages/providers/listener/onTxConfirmed.tsx +++ b/packages/demo/components/pages/providers/listener/onTxConfirmed.tsx @@ -6,6 +6,7 @@ import Codeblock from '../../../ui/codeblock'; import Input from '../../../ui/input'; import { Transaction } from '@meshsdk/core'; import { useWallet, CardanoWallet } from '@meshsdk/react'; +import { ArrowPathIcon, CheckIcon } from '@heroicons/react/24/solid'; export function onTxConfirmedLeft({ listenerName, address, lovelace }) { let code1 = `const tx = new Transaction({ initiator: wallet });\n`; @@ -98,17 +99,32 @@ export function onTxConfirmedRight({ )} {txWaitingConfrim === false && ( +
+ + Transaction pending confirmation +
+ )} + {txWaitingConfrim === true && ( +
+ + Transaction confirmed +
+ )} + + {/* {txWaitingConfrim === false && ( )} + {txWaitingConfrim === true && ( - )} + )} */} + diff --git a/packages/demo/pages/providers/blockfrost.tsx b/packages/demo/pages/providers/blockfrost.tsx index 917901c4..159c15e3 100644 --- a/packages/demo/pages/providers/blockfrost.tsx +++ b/packages/demo/pages/providers/blockfrost.tsx @@ -44,6 +44,8 @@ export default function ProvidersBlockfrost() { function Hero({ network, setNetwork }) { let code1 = `const blockfrostProvider = new BlockfrostProvider('');\n`; + let code2 = `const blockfrostProvider = new BlockfrostProvider('');\n`; + return (

@@ -65,10 +67,14 @@ function Hero({ network, setNetwork }) { Blockfrost {' '} provides restful APIs which allows your app to access information - stored on the blockchain. + stored on the blockchain. Get started:

-

Get started:

+

+ If you are using a privately hosted Blockfrost instance, you can set + the URL in the parameter: +

+

Choose network for this demo:

');\n`; + let code1 = `const koiosProvider = new KoiosProvider('<'api'|'preview'|'preprod'|'guild'>');\n`; return (
@@ -92,6 +92,16 @@ function Hero({ network, setNetwork }) { label: 'Preprod', onClick: () => setNetwork('preprod'), }, + { + key: 'preview', + label: 'Preview', + onClick: () => setNetwork('preview'), + }, + { + key: 'guild', + label: 'Guild', + onClick: () => setNetwork('guild'), + }, ]} currentSelected={network} /> diff --git a/packages/demo/pages/providers/ogmios.tsx b/packages/demo/pages/providers/ogmios.tsx index 68569c13..9c3ab10a 100644 --- a/packages/demo/pages/providers/ogmios.tsx +++ b/packages/demo/pages/providers/ogmios.tsx @@ -6,21 +6,15 @@ import { BadgeEvaluator, BadgeSubmitter, } from '../../components/pages/providers/badges'; -import { OgmiosProvider } from '@meshsdk/core'; +import { OgmiosProvider, Transaction } from '@meshsdk/core'; import Submitter from '../../components/pages/providers/submitter'; -// import ButtonGroup from '../../components/ui/buttongroup'; import { CardanoWallet, useWallet } from '@meshsdk/react'; +import Button from '../../components/ui/button'; +import Evaluator from '../../components/pages/providers/evaluator'; +import ButtonGroup from '../../components/ui/buttongroup'; export default function ProvidersOgmios() { - const sidebarItems = [ - // { label: 'Fetch Account Info', to: 'fetchAccountInfo' }, - // { label: 'Fetch Asset Addresses', to: 'fetchAssetAddresses' }, - // { label: 'Fetch Asset Metadata', to: 'fetchAssetMetadata' }, - // { label: 'Fetch Address Utxos', to: 'fetchAddressUtxos' }, - // { label: 'Fetch Handle Address', to: 'fetchHandleAddress' }, - // { label: 'Fetch Protocol Parameters', to: 'fetchProtocolParameters' }, - { label: 'Submit Tx', to: 'submitTx' }, - ]; + const sidebarItems = [{ label: 'Submit Tx', to: 'submitTx' }]; const [network, setNetwork] = useState('preprod'); return ( @@ -42,31 +36,33 @@ function Hero({ network, setNetwork }) { let code1 = `const ogmiosProvider = new OgmiosProvider();\n`; - // const ogmiosProvider = new OgmiosProvider( - // 'wss://ogmios-api.testnet.dandelion.link' - // ); - - // async function test() { - // const tx = new Transaction({ initiator: wallet }).sendLovelace( - // 'addr_test1qzmwuzc0qjenaljs2ytquyx8y8x02en3qxswlfcldwetaeuvldqg2n2p8y4kyjm8sqfyg0tpq9042atz0fr8c3grjmysm5e6yx', - // '1000000' - // ); - // const unsignedTx = await tx.build(); - // const signedTx = await wallet.signTx(unsignedTx); - // const txHash = await ogmiosProvider.submitTx(signedTx); - // console.log({ txHash }); - - // // const res = await ogmiosProvider.evaluateTx(signedTx); - // // console.log({ res }); - // } - - // useEffect(() => { - // if (connected) { - // ogmiosProvider.onNextTx((tx) => { - // console.log(111, 'ogmiosProvider.onNextTx', tx); - // }); - // } - // }, [connected]); + const ogmiosProvider = new OgmiosProvider(network); + + async function test() { + const tx = new Transaction({ initiator: wallet }).sendLovelace( + 'addr_test1qzmwuzc0qjenaljs2ytquyx8y8x02en3qxswlfcldwetaeuvldqg2n2p8y4kyjm8sqfyg0tpq9042atz0fr8c3grjmysm5e6yx', + '1000000' + ); + const unsignedTx = await tx.build(); + const signedTx = await wallet.signTx(unsignedTx); + const txHash = await ogmiosProvider.submitTx(signedTx); + console.log('txHash', { txHash }); + + // const res = await ogmiosProvider.evaluateTx(signedTx); + // console.log('ogmiosProvider.evaluateTx', { res }); + + ogmiosProvider.evaluateTx(signedTx).then(function (tx) { + console.log('ogmiosProvider.evaluateTx', tx); + }); + } + + useEffect(() => { + if (connected) { + ogmiosProvider.onNextTx((tx) => { + console.log('ogmiosProvider.onNextTx', tx); + }); + } + }, [connected]); return (
@@ -83,21 +79,46 @@ function Hero({ network, setNetwork }) { mini-protocols via JSON/RPC.

- {/* - */} -

- Browse the{' '} + Ogmios is a lightweight bridge interface for cardano-node. It offers + a WebSockets API that enables local clients to speak Ouroboros' + mini-protocols via JSON/RPC. Ogmios is a fast and lightweight + solution that can be deployed alongside relays to create entry + points on the Cardano network for various types of applications. + (reference:{' '} - Ogmios - {' '} - website. Get started: + ogmios.dev + + )

+

Get started:

+ +

Choose network for this demo:

+ setNetwork('mainnet'), + }, + { + key: 'preprod', + label: 'Preprod', + onClick: () => setNetwork('preprod'), + }, + ]} + currentSelected={network} + /> +
+
+

to remove, test only

+ + +
-
); @@ -116,7 +137,7 @@ function Main({ network }) { return ( <> - {/* */} + ); From 236861f2dfff7c1a0b1508afc926134c3dc0677e Mon Sep 17 00:00:00 2001 From: Abdelkrim Dib Date: Tue, 24 Jan 2023 13:51:59 +0100 Subject: [PATCH 21/27] allow custom url --- packages/module/src/providers/koios.provider.ts | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/packages/module/src/providers/koios.provider.ts b/packages/module/src/providers/koios.provider.ts index 40df5ba3..d3bf0e4b 100644 --- a/packages/module/src/providers/koios.provider.ts +++ b/packages/module/src/providers/koios.provider.ts @@ -14,10 +14,18 @@ import type { 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 { From f7f472ee2b2485b80e8a3b6634fe627ccafbf26b Mon Sep 17 00:00:00 2001 From: "Hong Jing (Jingles)" Date: Wed, 25 Jan 2023 10:23:24 +0800 Subject: [PATCH 22/27] ogmios evaluateTx --- .../components/pages/providers/evaluator.tsx | 29 ++--- .../pages/providers/evaluator/evaluateTx.tsx | 122 ++++++++++++------ packages/demo/pages/providers/ogmios.tsx | 46 +------ 3 files changed, 95 insertions(+), 102 deletions(-) diff --git a/packages/demo/components/pages/providers/evaluator.tsx b/packages/demo/components/pages/providers/evaluator.tsx index 12b89c65..359aec48 100644 --- a/packages/demo/components/pages/providers/evaluator.tsx +++ b/packages/demo/components/pages/providers/evaluator.tsx @@ -2,16 +2,13 @@ import { useEffect, useState } from 'react'; import SectionTwoCol from '../../common/sectionTwoCol'; import { demoAddresses } from '../../../configs/demo'; import { BadgeEvaluator } from './badges'; -import { - onTxConfirmedLeft, - onTxConfirmedRight, -} from './listener/onTxConfirmed'; +import { evaluateTxLeft, evaluateTxRight } from './evaluator/evaluateTx'; import { useWallet } from '@meshsdk/react'; export default function Evaluator({ evaluator, evaluatorName }) { const { wallet, connected } = useWallet(); const [address, setAddress] = useState(demoAddresses.testnet); - const [lovelace, setLovelace] = useState('5000000'); + // const [lovelace, setLovelace] = useState('5000000'); useEffect(() => { async function init() { @@ -28,24 +25,18 @@ export default function Evaluator({ evaluator, evaluatorName }) { return ( <> - {/* } - /> */} + /> ); } diff --git a/packages/demo/components/pages/providers/evaluator/evaluateTx.tsx b/packages/demo/components/pages/providers/evaluator/evaluateTx.tsx index c42a11d8..dd651642 100644 --- a/packages/demo/components/pages/providers/evaluator/evaluateTx.tsx +++ b/packages/demo/components/pages/providers/evaluator/evaluateTx.tsx @@ -3,67 +3,98 @@ import RunDemoButton from '../../../common/runDemoButton'; import RunDemoResult from '../../../common/runDemoResult'; import Card from '../../../ui/card'; import Codeblock from '../../../ui/codeblock'; -import Input from '../../../ui/input'; -import { Transaction } from '@meshsdk/core'; +import { Transaction, KoiosProvider, resolveDataHash } from '@meshsdk/core'; import { useWallet, CardanoWallet } from '@meshsdk/react'; -import { ArrowPathIcon, CheckIcon } from '@heroicons/react/24/solid'; - -export function evaluateTxLeft({ listenerName, txHash }) { - let code1 = `const tx = new Transaction({ initiator: wallet });\n`; - // code1 += `tx.sendLovelace('${address}', '${lovelace}');\n`; - // code1 += `\n`; - // code1 += `const unsignedTx = await tx.build();\n`; - // code1 += `const signedTx = await wallet.signTx(unsignedTx);\n`; - // code1 += `const txHash = await wallet.submitTx(signedTx);\n`; - // code1 += `\n`; - code1 += `${listenerName}.onTxConfirmed(txHash, () => {\n`; - code1 += ` console.log('Transaction confirmed');\n`; - code1 += `});\n`; + +export function evaluateTxLeft({ evaluatorName }) { + let code1 = ``; + code1 += `const unsignedTx = await tx.build();\n`; + code1 += `const evaluateTx = await ${evaluatorName}.evaluateTx(unsignedTx);\n`; + + let demoResults = ``; + demoResults += `[\n`; + demoResults += ` {\n`; + demoResults += ` "index": 0,\n`; + demoResults += ` "tag": "SPEND",\n`; + demoResults += ` "budget": {\n`; + demoResults += ` "mem": 1700\n`; + demoResults += ` }\n`; + demoResults += ` }\n`; + demoResults += `]\n`; return ( <>

- Allow you to listen to a transaction confirmation. Upon confirmation, - the callback will be called. + evaluateTx() accepts an unsigned transaction ( + unsignedTx) and it evaluates the resources required to + execute the transaction. Note that, this is only valid for transaction + interacting with redeemer (smart contract). By knowing the budget + required, you can use this to adjust the redeemer's budget so you don't + spend more than you need to execute transactions for this smart + contract.

+

+ Example responses from unlocking assets from the always succeed smart + contract. +

+ ); } -export function evaluateTxRight({ - listener, - txHash, - setTxHash, -}) { +export function evaluateTxRight({ evaluator }) { const { wallet, connected } = useWallet(); - const [loading, setLoading] = useState(false); const [response, setResponse] = useState(null); const [responseError, setResponseError] = useState(null); - const [txWaitingConfrim, setTxWaitingConfrim] = - useState(null); + + async function _getAssetUtxo({ scriptAddress, asset, datum }) { + const koios = new KoiosProvider('preprod'); + + const utxos = await koios.fetchAddressUTxOs(scriptAddress, asset); + + const dataHash = resolveDataHash(datum); + + let utxo = utxos.find((utxo: any) => { + return utxo.output.dataHash == dataHash; + }); + + return utxo; + } async function runDemo() { setLoading(true); setResponse(null); setResponseError(null); - setTxWaitingConfrim(null); try { - const tx = new Transaction({ initiator: wallet }); - // tx.sendLovelace(address, lovelace); + const assetUtxo = await _getAssetUtxo({ + scriptAddress: + 'addr_test1wpnlxv2xv9a9ucvnvzqakwepzl9ltx7jzgm53av2e9ncv4sysemm8', + asset: + '64af286e2ad0df4de2e7de15f8ff5b3d27faecf4ab2757056d860a424d657368546f6b656e', + datum: 'supersecretmeshdemo', + }); - const unsignedTx = await tx.build(); - const signedTx = await wallet.signTx(unsignedTx); - const txHash = await wallet.submitTx(signedTx); + const address = await wallet.getChangeAddress(); - setResponse(txHash); + const tx = new Transaction({ initiator: wallet }) + .redeemValue({ + value: assetUtxo, + script: { + version: 'V1', + code: '4e4d01000033222220051200120011', + }, + datum: 'supersecretmeshdemo', + }) + .sendValue(address, assetUtxo) + .setRequiredSigners([address]); - setTxWaitingConfrim(false); - listener.onTxConfirmed(txHash, () => { - setTxWaitingConfrim(true); - }); + const unsignedTx = await tx.build(); + const evaluateTx = await evaluator.evaluateTx(unsignedTx); + + setResponse(evaluateTx); } catch (error) { setResponseError(`${error}`); } @@ -73,18 +104,23 @@ export function evaluateTxRight({ return ( <> - +

+ Unlock an asset from the always succeed to check how much it takes to + execute this transaction. +

{connected ? ( - + <> + + ) : ( )} - +
diff --git a/packages/demo/pages/providers/ogmios.tsx b/packages/demo/pages/providers/ogmios.tsx index 9c3ab10a..26898ec0 100644 --- a/packages/demo/pages/providers/ogmios.tsx +++ b/packages/demo/pages/providers/ogmios.tsx @@ -6,15 +6,16 @@ import { BadgeEvaluator, BadgeSubmitter, } from '../../components/pages/providers/badges'; -import { OgmiosProvider, Transaction } from '@meshsdk/core'; +import { OgmiosProvider } from '@meshsdk/core'; import Submitter from '../../components/pages/providers/submitter'; -import { CardanoWallet, useWallet } from '@meshsdk/react'; -import Button from '../../components/ui/button'; import Evaluator from '../../components/pages/providers/evaluator'; import ButtonGroup from '../../components/ui/buttongroup'; export default function ProvidersOgmios() { - const sidebarItems = [{ label: 'Submit Tx', to: 'submitTx' }]; + const sidebarItems = [ + { label: 'Evaluate Tx', to: 'evaluateTx' }, + { label: 'Submit Tx', to: 'submitTx' }, + ]; const [network, setNetwork] = useState('preprod'); return ( @@ -32,38 +33,8 @@ export default function ProvidersOgmios() { } function Hero({ network, setNetwork }) { - const { wallet, connected } = useWallet(); - let code1 = `const ogmiosProvider = new OgmiosProvider();\n`; - const ogmiosProvider = new OgmiosProvider(network); - - async function test() { - const tx = new Transaction({ initiator: wallet }).sendLovelace( - 'addr_test1qzmwuzc0qjenaljs2ytquyx8y8x02en3qxswlfcldwetaeuvldqg2n2p8y4kyjm8sqfyg0tpq9042atz0fr8c3grjmysm5e6yx', - '1000000' - ); - const unsignedTx = await tx.build(); - const signedTx = await wallet.signTx(unsignedTx); - const txHash = await ogmiosProvider.submitTx(signedTx); - console.log('txHash', { txHash }); - - // const res = await ogmiosProvider.evaluateTx(signedTx); - // console.log('ogmiosProvider.evaluateTx', { res }); - - ogmiosProvider.evaluateTx(signedTx).then(function (tx) { - console.log('ogmiosProvider.evaluateTx', tx); - }); - } - - useEffect(() => { - if (connected) { - ogmiosProvider.onNextTx((tx) => { - console.log('ogmiosProvider.onNextTx', tx); - }); - } - }, [connected]); - return (

@@ -113,12 +84,7 @@ function Hero({ network, setNetwork }) { currentSelected={network} /> -
-

to remove, test only

- - - -
+

); From 96ece4ab0a59afd595431ac974b5e3f6c48b59eb Mon Sep 17 00:00:00 2001 From: "Hong Jing (Jingles)" Date: Wed, 25 Jan 2023 10:27:56 +0800 Subject: [PATCH 23/27] update koios --- packages/demo/pages/providers/koios.tsx | 8 +++++++- packages/demo/public/sitemap.xml | 12 ++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/packages/demo/pages/providers/koios.tsx b/packages/demo/pages/providers/koios.tsx index 9154e007..ad430780 100644 --- a/packages/demo/pages/providers/koios.tsx +++ b/packages/demo/pages/providers/koios.tsx @@ -44,7 +44,8 @@ export default function ProvidersKoios() { } function Hero({ network, setNetwork }) { - let code1 = `const koiosProvider = new KoiosProvider('<'api'|'preview'|'preprod'|'guild'>');\n`; + let code1 = `const koiosProvider = new KoiosProvider('');\n`; + let code2 = `const koiosProvider = new KoiosProvider('');\n`; return (
@@ -79,6 +80,11 @@ function Hero({ network, setNetwork }) { >

Get started:

+

+ If you are using a privately hosted instance, you can set the URL in + the parameter: +

+

Choose network for this demo:

https://meshjs.dev/providers + + https://meshjs.dev/providers/blockfrost + + + https://meshjs.dev/providers/tangocrypto + + + https://meshjs.dev/providers/koios + + + https://meshjs.dev/providers/ogmios + https://meshjs.dev/apis/resolvers From e012dedc92ae5820ead56a76d19e241a91dd691a Mon Sep 17 00:00:00 2001 From: Abdelkrim Dib Date: Wed, 25 Jan 2023 10:28:36 +0100 Subject: [PATCH 24/27] fix steps not showing up --- packages/module/src/providers/ogmios.provider.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/module/src/providers/ogmios.provider.ts b/packages/module/src/providers/ogmios.provider.ts index 2239731b..9cb2ca4c 100644 --- a/packages/module/src/providers/ogmios.provider.ts +++ b/packages/module/src/providers/ogmios.provider.ts @@ -34,7 +34,7 @@ export class OgmiosProvider implements IEvaluator, ISubmitter { tag: key.split(':')[0].toUpperCase(), budget: { mem: result.EvaluationResult[key].memory, - steps: result.EvaluationResult[key].cpu, + steps: result.EvaluationResult[key].steps, }, } ) From 1b8e9e70c01177881b47704211b8a07718cea42d Mon Sep 17 00:00:00 2001 From: "Hong Jing (Jingles)" Date: Wed, 25 Jan 2023 20:47:16 +0800 Subject: [PATCH 25/27] update evaluateTx and tango --- .../pages/providers/evaluator/evaluateTx.tsx | 17 ++++++++++++++++- packages/demo/pages/providers/tangocrypto.tsx | 10 +++++++++- 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/packages/demo/components/pages/providers/evaluator/evaluateTx.tsx b/packages/demo/components/pages/providers/evaluator/evaluateTx.tsx index dd651642..0da43bf5 100644 --- a/packages/demo/components/pages/providers/evaluator/evaluateTx.tsx +++ b/packages/demo/components/pages/providers/evaluator/evaluateTx.tsx @@ -17,11 +17,21 @@ export function evaluateTxLeft({ evaluatorName }) { demoResults += ` "index": 0,\n`; demoResults += ` "tag": "SPEND",\n`; demoResults += ` "budget": {\n`; - demoResults += ` "mem": 1700\n`; + demoResults += ` "mem": 1700,\n`; + demoResults += ` "steps": 368100\n`; demoResults += ` }\n`; demoResults += ` }\n`; demoResults += `]\n`; + let codeRedeemer = ``; + codeRedeemer += `const redeemer = {\n`; + codeRedeemer += ` data: { alternative: 0, fields: [...] },\n`; + codeRedeemer += ` budget: {\n`; + codeRedeemer += ` mem: 1700,\n`; + codeRedeemer += ` steps: 368100,\n`; + codeRedeemer += ` },\n`; + codeRedeemer += `};\n`; + return ( <>

@@ -39,6 +49,11 @@ export function evaluateTxLeft({ evaluatorName }) { contract.

+

+ With the mem and steps, you can refine the + budget for the redeemer. For example: +

+ ); } diff --git a/packages/demo/pages/providers/tangocrypto.tsx b/packages/demo/pages/providers/tangocrypto.tsx index 7228628e..83d07880 100644 --- a/packages/demo/pages/providers/tangocrypto.tsx +++ b/packages/demo/pages/providers/tangocrypto.tsx @@ -6,12 +6,14 @@ import { BadgeFetcher, BadgeSubmitter, BadgeListener, + BadgeEvaluator, } from '../../components/pages/providers/badges'; import Fetcher from '../../components/pages/providers/fetcher'; import { TangoProvider } from '@meshsdk/core'; import Submitter from '../../components/pages/providers/submitter'; import ButtonGroup from '../../components/ui/buttongroup'; import Listener from '../../components/pages/providers/listener'; +import Evaluator from '../../components/pages/providers/evaluator'; export default function ProvidersTangocrypto() { const sidebarItems = [ @@ -25,6 +27,7 @@ export default function ProvidersTangocrypto() { { label: 'Fetch Transaction Info', to: 'fetchTxInfo' }, { label: 'Submit Tx', to: 'submitTx' }, { label: 'On Transaction Confirmed', to: 'onTxConfirmed' }, + { label: 'Evaluate Tx', to: 'evaluateTx' }, ]; const [network, setNetwork] = useState('preprod'); @@ -45,7 +48,7 @@ export default function ProvidersTangocrypto() { function Hero({ network, setNetwork }) { let codeTango = `import { TangoProvider } from '@meshsdk/core';\n\n`; codeTango += `const tangocryptoProvider = new TangoProvider(\n`; - codeTango += ` '',\n`; + codeTango += ` '',\n`; codeTango += ` ''\n`; codeTango += ` ''\n`; codeTango += `);`; @@ -58,6 +61,7 @@ function Hero({ network, setNetwork }) { +

@@ -138,6 +142,10 @@ function Main({ network }) { listener={tangocryptoProvider} listenerName="tangocryptoProvider" /> + ); } From 8bc44bd7fe923417c5030f913f8bcb0176e5a721 Mon Sep 17 00:00:00 2001 From: "Hong Jing (Jingles)" Date: Wed, 25 Jan 2023 21:07:11 +0800 Subject: [PATCH 26/27] add signin starter --- .../components/pages/starterTemplates/data.ts | 18 +++++++---- .../pages/starterTemplates/index.tsx | 28 ++++++++++++++++++ .../pages/starterTemplates/selector.tsx | 4 +-- packages/demo/public/templates/signin.png | Bin 0 -> 2578 bytes 4 files changed, 42 insertions(+), 8 deletions(-) create mode 100644 packages/demo/public/templates/signin.png diff --git a/packages/demo/components/pages/starterTemplates/data.ts b/packages/demo/components/pages/starterTemplates/data.ts index 8f64bf4e..030f613c 100644 --- a/packages/demo/components/pages/starterTemplates/data.ts +++ b/packages/demo/components/pages/starterTemplates/data.ts @@ -17,12 +17,18 @@ export const templates = { cli: 'staking', image: 'stakepool.png', }, - marketplace: { - title: 'Marketplace', - desc: `Start a new marketplace with prebuilt marketplace contract.`, - cli: 'marketplace', - image: 'marketplace.png', - comingsoon: true, + // marketplace: { + // title: 'Marketplace', + // desc: `Start a new marketplace with prebuilt marketplace contract.`, + // cli: 'marketplace', + // image: 'marketplace.png', + // comingsoon: true, + // }, + signin: { + title: 'Sign In with Wallet', + desc: `Cryptographically prove the ownership of a wallet.`, + cli: 'signin', + image: 'signin.png', }, }; diff --git a/packages/demo/components/pages/starterTemplates/index.tsx b/packages/demo/components/pages/starterTemplates/index.tsx index 4342bb84..739929c2 100644 --- a/packages/demo/components/pages/starterTemplates/index.tsx +++ b/packages/demo/components/pages/starterTemplates/index.tsx @@ -128,6 +128,34 @@ const items = [

), }, + { + title: 'Sign In Next.js TypeScript', + template: 'signin', + framework: 'next', + language: 'typescript', + installCode: 'npx create-mesh-app leap -t signin -s next -l ts', + demoUrl: 'http://signin-template.meshjs.dev/', + repoUrl: 'https://github.com/MeshJS/signin-next-ts-template', + desc: ( +

+ Cryptographically prove the ownership of a wallet by signing a piece of data using data sign. +

+ ), + }, + { + title: 'Sign In Next.js JavaScript', + template: 'signin', + framework: 'next', + language: 'javascript', + installCode: 'npx create-mesh-app leap -t signin -s next -l js', + demoUrl: 'http://signin-template.meshjs.dev/', + repoUrl: 'https://github.com/MeshJS/signin-next-js-template', + desc: ( +

+ Cryptographically prove the ownership of a wallet by signing a piece of data using data sign. +

+ ), + }, ]; export default function ReactStarterTemplates() { diff --git a/packages/demo/components/pages/starterTemplates/selector.tsx b/packages/demo/components/pages/starterTemplates/selector.tsx index 563c13fd..41b9570b 100644 --- a/packages/demo/components/pages/starterTemplates/selector.tsx +++ b/packages/demo/components/pages/starterTemplates/selector.tsx @@ -18,13 +18,13 @@ export default function Selector({ setSelected={setSelectedTemplate} /> - + /> */} E#4E@N|37}0b zFP-5X@@tQ|g72-=sL9Z2?8;w0W59bN*KyTXjISFU&6n*&*XL|e$R2G`R5RCd8MBM` z`%<|U^K(Jq-zTm5)S5JVjr#E~wYe=n;v1LUQ=U7jCRA$Vb>$BrR=GL@UHRnxi``k@ zEgn~Duw!|n{h9HL-z8V+e06q7y?%{ZU#FtOHrHQFE>myv_WN1nDw}2SRxQJLGJV!-rk z0j9!X5Z<67o;<$&a6>cTqcY?Dwt*Iwi5>+RXVUFs+SF(G>oLB82pLaQ; zBKE=yFI`EfzXbJIYjn_hUF1d3v>tn>EJg84xMui@7UWf&HDmXoB@kisu4u0{$Gjyp z1@DS6y}|eAHedSl48Yn(M=%7W?q`(e1U*dOmK8r0qCfm^}K* z(dMr1%7t6h^kHb?4af)nZu0~9s4%ga`MrCc9?zpBoG0LIQ4hXfI=2pxiZOcJsYrdU zYxztwIIx{0x8e!uPzUtFGSt^ zklLzs<<6}<5@^{EiX)|%hWHT846@+yl=s#(tc8ssRM1AAG}jvGF}ki)P;}%GU|eT1 zVA%QFd9gKJdr`$N?5CRQn-7c^!ILyyT~Nm7RLaQPF{)9FR_X%Co&HIeX#9%PZGy<` z56_MnkvLv7yG{`Dsv`+O#HjqQbIu%HUb*&HCJewi<`La@HH`#}4|4j$u^l;5A?2fr z(dw6zaY`B=T5@Bgw30>P39h;Axno2V(&`ml)6YnE`Bj2k4`PF(V17?(`>D z^uZwrH*m;I8`OOVleYDGt?7bPxcNKmh=RPV3i9nhqydMJt<3OG#%1a0c0#8UKh|gL zBwe^q3X+${3ar179&X72$6H8&E;mo>SJ7;5Rh7J!t|ch{9QENOO(hA1mpj#1potld zb%=_u2FYsx^$C8>5RaO#hG_*-`q+|;!|&%<-jRBo{Wn0dS=^qm8yTm2^x#jpLtFBX zHg91lo-COePgt@b2I4Jo^e$JL9aHE}VxLrfl{R1P%M&8-paTVd)Q$t*#u1k?+t|CdxViBai?Yb;V+rrx@;CiORX@37 zB@y${N31yJ<#D4+_V0!N?)X;2r#BWXTkRMsRuFQ#vV=3e0?P>u0oTe}WZTI{%qO#T*B0sX^{IlUi+u9+o>(}wjHQ272n3x zp79lhE*VB(mkMpw+F9%jqf>@957EB_rZU#fmd#-Ia! zu93ve*>ET*!51?5Fle0fjkl4)Spx?2Wa14jU;iLcX(x7eKLbQF+Jlb~dep7w*zh{&97k{-41|X literal 0 HcmV?d00001 From 492bebddab8897e12976ae0cc960275b9abef38f Mon Sep 17 00:00:00 2001 From: Abdelkrim Dib Date: Wed, 25 Jan 2023 16:19:26 +0100 Subject: [PATCH 27/27] pump up the version --- packages/cli/package.json | 2 +- packages/cli/src/actions/create.ts | 2 +- packages/demo/package.json | 4 ++-- packages/module/package.json | 2 +- packages/react/package.json | 4 ++-- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/cli/package.json b/packages/cli/package.json index e4ba2d2d..0d710b17 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -3,7 +3,7 @@ "description": "A quick and easy way to bootstrap your dApps on Cardano using Mesh.", "homepage": "https://meshjs.dev", "author": "MeshJS", - "version": "1.0.4", + "version": "1.0.5", "license": "Apache-2.0", "main": "dist/create-mesh-app.cjs.js", "bin": { diff --git a/packages/cli/src/actions/create.ts b/packages/cli/src/actions/create.ts index 8c58286f..06635d2a 100644 --- a/packages/cli/src/actions/create.ts +++ b/packages/cli/src/actions/create.ts @@ -17,7 +17,7 @@ export const create = async (name, options) => { { title: 'Starter Project', value: 'starter' }, { title: 'Multi-Sig Minting', value: 'minting' }, { title: 'Stake-Pool Website', value: 'staking' }, - { title: 'Smart-Contract Marketplace ', value: 'marketplace' }, + { title: 'Cardano Sign-In', value: 'signin' }, ])); const stack = diff --git a/packages/demo/package.json b/packages/demo/package.json index 6f4b6b8c..3163db23 100644 --- a/packages/demo/package.json +++ b/packages/demo/package.json @@ -14,8 +14,8 @@ "dependencies": { "@headlessui/react": "^1.6.6", "@heroicons/react": "^2.0.10", - "@meshsdk/core": "1.3.0", - "@meshsdk/react": "1.1.3", + "@meshsdk/core": "1.4.0", + "@meshsdk/react": "1.1.4", "copy-to-clipboard": "^3.3.2", "flowbite": "^1.5.3", "flowbite-react": "^0.1.10", diff --git a/packages/module/package.json b/packages/module/package.json index 1e93bd3d..85557dae 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/react/package.json b/packages/react/package.json index 471d5434..fff973fe 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" },