Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: input-output-hk/cardano-js-sdk
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: @cardano-sdk/[email protected]
Choose a base ref
...
head repository: input-output-hk/cardano-js-sdk
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: @cardano-sdk/[email protected]
Choose a head ref
  • 8 commits
  • 55 files changed
  • 4 contributors

Commits on Mar 17, 2025

  1. feat: discover shared wallet by metadata label

    greatertomi committed Mar 17, 2025

    Verified

    This commit was signed with the committer’s verified signature.
    greatertomi John Oshalusi
    Copy the full SHA
    ff3c6d6 View commit details
  2. Merge pull request #1608 from input-output-hk/feat/lw-12383-shared-wa…

    …llet-discovery
    
    feat: discover shared wallet by metadata label
    rhyslbw authored Mar 17, 2025

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    91a31d9 View commit details

Commits on Apr 7, 2025

  1. feat(cardano-services-client): restore KoraLabsHandleProvider

    Does not re-engage in e2e tests
    rhyslbw authored and iccicci committed Apr 7, 2025

    Verified

    This commit was signed with the committer’s verified signature.
    iccicci Daniele Ricci
    Copy the full SHA
    d81aaeb View commit details
  2. Merge pull request #1609 from input-output-hk/feat/restore-kora-labs-…

    …handle-provider
    
    LW-12083 feat(cardano-services-client): restore KoraLabsHandleProvider
    rhyslbw authored Apr 7, 2025

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    f8e8b24 View commit details

Commits on Apr 8, 2025

  1. Verified

    This commit was signed with the committer’s verified signature.
    iccicci Daniele Ricci
    Copy the full SHA
    f13f9d3 View commit details
  2. test(e2e): add tests for subhandles and virtual subhandles

    iccicci committed Apr 8, 2025

    Verified

    This commit was signed with the committer’s verified signature.
    iccicci Daniele Ricci
    Copy the full SHA
    af50934 View commit details
  3. Merge pull request #1613 from input-output-hk/test/LW-12621-subhandle

    LW-12621 subhandles and virtual subhandles tests
    rhyslbw authored Apr 8, 2025

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    a7a1a9c View commit details
  4. ci: publish packages [skip actions]

     - @cardano-sdk/[email protected]
     - @cardano-sdk/[email protected]
     - @cardano-sdk/[email protected]
     - @cardano-sdk/[email protected]
     - @cardano-sdk/[email protected]
     - @cardano-sdk/[email protected]
     - @cardano-sdk/[email protected]
     - @cardano-sdk/[email protected]
     - @cardano-sdk/[email protected]
     - @cardano-sdk/[email protected]
     - @cardano-sdk/[email protected]
     - @cardano-sdk/[email protected]
     - @cardano-sdk/[email protected]
     - @cardano-sdk/[email protected]
     - @cardano-sdk/[email protected]
     - @cardano-sdk/[email protected]
     - @cardano-sdk/[email protected]
     - @cardano-sdk/[email protected]
     - @cardano-sdk/[email protected]
    lace-bot committed Apr 8, 2025

    Verified

    This commit was signed with the committer’s verified signature.
    lace-bot Lace Bot
    Copy the full SHA
    17add5a View commit details
Showing with 730 additions and 22 deletions.
  1. +1 −0 .github/workflows/continuous-integration-unit-tests.yaml
  2. +7 −0 packages/cardano-services-client/CHANGELOG.md
  3. +2 −1 packages/cardano-services-client/package.json
  4. +101 −0 packages/cardano-services-client/src/HandleProvider/KoraLabsHandleProvider.ts
  5. +1 −0 packages/cardano-services-client/src/HandleProvider/index.ts
  6. +57 −0 packages/cardano-services-client/src/SharedWalletProvider/BlockfrostSharedWalletProvider.ts
  7. +1 −0 packages/cardano-services-client/src/SharedWalletProvider/index.ts
  8. +32 −0 packages/cardano-services-client/src/SharedWalletProvider/types.ts
  9. +1 −0 packages/cardano-services-client/src/index.ts
  10. +110 −0 packages/cardano-services-client/test/HandleProvider/KoraLabsHandleProvider.test.ts
  11. +175 −0 packages/cardano-services-client/test/SharedWalletProvider/BlockfrostSharedWallet.test.ts
  12. +45 −0 packages/cardano-services-client/test/util.ts
  13. +4 −0 packages/cardano-services/CHANGELOG.md
  14. +1 −1 packages/cardano-services/package.json
  15. +6 −0 packages/core/CHANGELOG.md
  16. +1 −1 packages/core/package.json
  17. +2 −0 packages/core/src/const.ts
  18. +1 −0 packages/core/src/index.ts
  19. +4 −0 packages/dapp-connector/CHANGELOG.md
  20. +1 −1 packages/dapp-connector/package.json
  21. +6 −0 packages/e2e/CHANGELOG.md
  22. +1 −0 packages/e2e/jest.config.js
  23. +2 −1 packages/e2e/package.json
  24. +84 −0 packages/e2e/test/handle/KoraLabsHandleProvider.test.ts
  25. +4 −0 packages/golden-test-generator/CHANGELOG.md
  26. +1 −1 packages/golden-test-generator/package.json
  27. +4 −0 packages/governance/CHANGELOG.md
  28. +1 −1 packages/governance/package.json
  29. +4 −0 packages/hardware-ledger/CHANGELOG.md
  30. +1 −1 packages/hardware-ledger/package.json
  31. +4 −0 packages/hardware-trezor/CHANGELOG.md
  32. +1 −1 packages/hardware-trezor/package.json
  33. +4 −0 packages/input-selection/CHANGELOG.md
  34. +1 −1 packages/input-selection/package.json
  35. +6 −0 packages/key-management/CHANGELOG.md
  36. +1 −1 packages/key-management/package.json
  37. +3 −3 packages/key-management/src/types.ts
  38. +4 −0 packages/ogmios/CHANGELOG.md
  39. +1 −1 packages/ogmios/package.json
  40. +4 −0 packages/projection-typeorm/CHANGELOG.md
  41. +1 −1 packages/projection-typeorm/package.json
  42. +4 −0 packages/projection/CHANGELOG.md
  43. +1 −1 packages/projection/package.json
  44. +4 −0 packages/tx-construction/CHANGELOG.md
  45. +1 −1 packages/tx-construction/package.json
  46. +4 −0 packages/util-dev/CHANGELOG.md
  47. +1 −1 packages/util-dev/package.json
  48. +4 −0 packages/util-rxjs/CHANGELOG.md
  49. +1 −1 packages/util-rxjs/package.json
  50. +4 −0 packages/wallet/CHANGELOG.md
  51. +1 −1 packages/wallet/package.json
  52. +4 −0 packages/web-extension/CHANGELOG.md
  53. +1 −1 packages/web-extension/package.json
  54. +1 −0 yarn-project.nix
  55. +8 −0 yarn.lock
1 change: 1 addition & 0 deletions .github/workflows/continuous-integration-unit-tests.yaml
Original file line number Diff line number Diff line change
@@ -42,5 +42,6 @@ jobs:
- name: 🔬 Test
run: |
yarn test --forceExit
yarn test:handle
env:
NODE_OPTIONS: '--max_old_space_size=8192'
7 changes: 7 additions & 0 deletions packages/cardano-services-client/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -3,6 +3,13 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.

## [0.26.9](https://github.com/input-output-hk/cardano-js-sdk/compare/@cardano-sdk/cardano-services-client@0.26.8...@cardano-sdk/cardano-services-client@0.26.9) (2025-04-08)

### Features

* **cardano-services-client:** restore KoraLabsHandleProvider ([d81aaeb](https://github.com/input-output-hk/cardano-js-sdk/commit/d81aaeb1b7a5f0daf36987c41f055f40805079d6))
* discover shared wallet by metadata label ([ff3c6d6](https://github.com/input-output-hk/cardano-js-sdk/commit/ff3c6d66f1d1fd5cc7983086587328884945b40a))

## [0.26.8](https://github.com/input-output-hk/cardano-js-sdk/compare/@cardano-sdk/cardano-services-client@0.26.7...@cardano-sdk/cardano-services-client@0.26.8) (2025-03-03)

**Note:** Version bump only for package @cardano-sdk/cardano-services-client
3 changes: 2 additions & 1 deletion packages/cardano-services-client/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@cardano-sdk/cardano-services-client",
"version": "0.26.8",
"version": "0.26.9",
"description": "Cardano Services Client",
"engines": {
"node": ">=16.20.2"
@@ -60,6 +60,7 @@
"@cardano-sdk/core": "workspace:~",
"@cardano-sdk/crypto": "workspace:~",
"@cardano-sdk/util": "workspace:~",
"@koralabs/handles-public-api-interfaces": "^2.13.0",
"axios": "^1.7.4",
"class-validator": "^0.14.0",
"isomorphic-ws": "^5.0.0",
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
// cSpell:ignore kora koralabs
import {
Asset,
Cardano,
HandleProvider,
HandleResolution,
HealthCheckResponse,
ProviderError,
ProviderFailure,
ResolveHandlesArgs
} from '@cardano-sdk/core';

// eslint-disable-next-line import/no-extraneous-dependencies
import { IHandle } from '@koralabs/handles-public-api-interfaces';
import axios, { AxiosAdapter, AxiosInstance } from 'axios';

/** The KoraLabsHandleProvider endpoint paths. */
const paths = {
handles: '/handles',
healthCheck: '/health'
};

export interface KoraLabsHandleProviderDeps {
serverUrl: string;
adapter?: AxiosAdapter;
policyId: Cardano.PolicyId;
}

export const toHandleResolution = ({
apiResponse,
policyId
}: {
apiResponse: IHandle;
policyId: Cardano.PolicyId;
}): HandleResolution => ({
backgroundImage: apiResponse.bg_image ? Asset.Uri(apiResponse.bg_image) : undefined,
cardanoAddress: Cardano.PaymentAddress(apiResponse.resolved_addresses.ada),
handle: apiResponse.name,
hasDatum: apiResponse.has_datum,
image: apiResponse.image ? Asset.Uri(apiResponse.image) : undefined,
policyId,
profilePic: apiResponse.pfp_image ? Asset.Uri(apiResponse.pfp_image) : undefined
});

/**
* Creates a KoraLabs Provider instance to resolve Standard Handles
*
* @param KoraLabsHandleProviderDeps The configuration object fot the KoraLabs Handle Provider.
*/
export class KoraLabsHandleProvider implements HandleProvider {
private axiosClient: AxiosInstance;
policyId: Cardano.PolicyId;

constructor({ serverUrl, adapter, policyId }: KoraLabsHandleProviderDeps) {
this.axiosClient = axios.create({
adapter,
baseURL: serverUrl
});
this.policyId = policyId;
}

resolveHandles({ handles }: ResolveHandlesArgs): Promise<Array<HandleResolution | null>> {
// eslint-disable-next-line unicorn/consistent-function-scoping
const resolveHandle = async (handle: string) => {
try {
const { data } = await this.axiosClient.get<IHandle>(`${paths.handles}/${handle}`);

return toHandleResolution({ apiResponse: data, policyId: this.policyId });
} catch (error) {
if (error instanceof ProviderError) throw error;
if (axios.isAxiosError(error)) {
if (error.response?.status === 404) return null;
if (error.request) throw new ProviderError(ProviderFailure.ConnectionFailure, error, error.code);

throw new ProviderError(
ProviderFailure.Unhealthy,
error,
`Failed to resolve handles due to: ${error.message}`
);
}

throw new ProviderError(ProviderFailure.Unknown, error, 'Failed to resolve handles');
}
};

return Promise.all(handles.map((handle) => resolveHandle(handle)));
}

async healthCheck(): Promise<HealthCheckResponse> {
try {
await this.axiosClient.get(`${paths.healthCheck}`);
return { ok: true };
} catch {
return { ok: false };
}
}

async getPolicyIds(): Promise<Cardano.PolicyId[]> {
return [this.policyId];
}
}
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export * from './KoraLabsHandleProvider';
export * from './handleHttpProvider';
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import * as Crypto from '@cardano-sdk/crypto';
import { BlockfrostClient, BlockfrostProvider, fetchSequentially } from '../blockfrost';
import { Cardano, MULTISIG_CIP_ID, Serialization } from '@cardano-sdk/core';
import { Logger } from 'ts-log';
import { MultiSigRegistration, MultiSigTransaction, SharedWalletProvider } from './types';
import type { Responses } from '@blockfrost/blockfrost-js';

const MULTI_SIG_LABEL = MULTISIG_CIP_ID;

const isMultiSigRegistration = (metadata: unknown): metadata is MultiSigRegistration =>
!!metadata && typeof metadata === 'object' && 'participants' in metadata;

export class BlockfrostSharedWalletProvider extends BlockfrostProvider implements SharedWalletProvider {
constructor(client: BlockfrostClient, logger: Logger) {
super(client, logger);
}

private async getNativeScripts(txId: Cardano.TransactionId): Promise<Cardano.Script[]> {
const response = await this.request<Responses['tx_content_cbor']>(`txs/${txId}/cbor`);
const transaction = Serialization.Transaction.fromCbor(Serialization.TxCBOR(response.cbor)).toCore();
return transaction.auxiliaryData?.scripts ?? [];
}

async discoverWallets(pubKey: Crypto.Ed25519KeyHashHex): Promise<MultiSigTransaction[]> {
const batchSize = 100;

const multiSigTransactions = await fetchSequentially<Responses['tx_metadata_label_json'][0], MultiSigTransaction>(
{
haveEnoughItems: (wallets, _) => wallets.length < batchSize,
paginationOptions: { count: batchSize },
request: (paginationQueryString) =>
this.request<Responses['tx_metadata_label_json']>(
`metadata/txs/labels/${MULTI_SIG_LABEL}?${paginationQueryString}`
),
responseTranslator: (wallets) =>
wallets
.filter((wallet) => {
const metadata = wallet.json_metadata;
return isMultiSigRegistration(metadata) && metadata?.participants?.[pubKey];
})
.map((wallet) => ({
metadata: wallet.json_metadata as unknown as MultiSigRegistration,
nativeScripts: [],
txId: Cardano.TransactionId(wallet.tx_hash)
}))
},
[]
);

return await Promise.all(
multiSigTransactions.map(async (wallet) => ({
...wallet,
nativeScripts: await this.getNativeScripts(wallet.txId)
}))
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './BlockfrostSharedWalletProvider';
32 changes: 32 additions & 0 deletions packages/cardano-services-client/src/SharedWalletProvider/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { Cardano } from '@cardano-sdk/core';
import { Ed25519KeyHashHex } from '@cardano-sdk/crypto';

type ScriptType = number;

interface MultiSigParticipant {
name: string;
description?: string;
icon?: string;
}

interface MultiSigParticipants {
[key: Ed25519KeyHashHex]: MultiSigParticipant;
}

export interface MultiSigRegistration {
types: ScriptType[];
name?: string;
description?: string;
icon?: string;
participants?: MultiSigParticipants;
}

export interface MultiSigTransaction {
txId: Cardano.TransactionId;
metadata: MultiSigRegistration;
nativeScripts?: Cardano.Script[];
}

export interface SharedWalletProvider {
discoverWallets: (pubKey: Ed25519KeyHashHex) => Promise<MultiSigTransaction[]>;
}
1 change: 1 addition & 0 deletions packages/cardano-services-client/src/index.ts
Original file line number Diff line number Diff line change
@@ -10,6 +10,7 @@ export * from './RewardAccountInfoProvider';
export * from './NetworkInfoProvider';
export * from './RewardsProvider';
export * from './HandleProvider';
export * from './SharedWalletProvider';
export * from './version';
export * from './WebSocket';
export {
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
/* eslint-disable no-magic-numbers */
/* eslint-disable camelcase */
import { Cardano, ProviderError } from '@cardano-sdk/core';
import { KoraLabsHandleProvider } from '../../src';
import {
getAliceHandleAPIResponse,
getAliceHandleProviderResponse,
getBobHandleAPIResponse,
getBobHandleProviderResponse
} from '../util';
import MockAdapter from 'axios-mock-adapter';
import axios from 'axios';

const config = {
policyId: Cardano.PolicyId('50fdcdbfa3154db86a87e4b5697ae30d272e0bbcfa8122efd3e301cb'),
serverUrl: 'http://some-hostname:3000'
};

describe('KoraLabsHandleProvider', () => {
let axiosMock: MockAdapter;
let provider: KoraLabsHandleProvider;

beforeAll(() => {
axiosMock = new MockAdapter(axios);
provider = new KoraLabsHandleProvider(config);
});

afterEach(() => {
axiosMock.reset();
});

afterAll(() => {
axiosMock.restore();
});

describe('resolveHandles', () => {
test('HandleProvider should resolve a single handle', async () => {
axiosMock.onGet().replyOnce(200, getAliceHandleAPIResponse);
const args = {
handles: ['alice']
};
await expect(provider.resolveHandles(args)).resolves.toEqual([getAliceHandleProviderResponse]);
});

test('HandleProvider should resolve multiple handles', async () => {
axiosMock.onGet().replyOnce(200, getAliceHandleAPIResponse).onGet().replyOnce(200, getBobHandleAPIResponse);
const args = {
handles: ['alice', 'bob']
};
await expect(provider.resolveHandles(args)).resolves.toEqual([
getAliceHandleProviderResponse,
getBobHandleProviderResponse
]);
});
});

describe('error checks', () => {
test('HandleProvider should throw ProviderError with ConnectionFailure on request error', async () => {
axiosMock.onGet('/handles/alice').networkError();
const args = { handles: ['alice'] };
await expect(provider.resolveHandles(args)).rejects.toThrowError(ProviderError);
});
test('HandleProvider should return null for 404 response from API', async () => {
axiosMock.onGet('/handles/alice').reply(404);
const args = { handles: ['alice'] };
await expect(provider.resolveHandles(args)).resolves.toEqual([null]);
});
test('HandleProvider should throw ProviderError with Unhealthy on other Axios error', async () => {
axiosMock.onGet('/handles/bob').reply(500);
const args = { handles: ['bob'] };
await expect(provider.resolveHandles(args)).rejects.toThrowError(ProviderError);
});
test('HandleProvider should throw ProviderError', async () => {
axiosMock.onGet('/handles/bob').networkError();
const args = { handles: ['bob'] };
await expect(provider.resolveHandles(args)).rejects.toThrowError(ProviderError);
});
test('HandleProvider should throw ProviderError with Unknown, unable to resolve handle', async () => {
axiosMock.onGet().replyOnce(304, getAliceHandleAPIResponse);
const args = { handles: ['bob'] };
await expect(provider.resolveHandles(args)).rejects.toThrowError(ProviderError);
});
});

describe('health checks', () => {
test('HandleProvider should get ok health check', async () => {
axiosMock.onGet().replyOnce(200, {});
const result = await provider.healthCheck();
expect(result.ok).toEqual(true);
});

test('HandleProvider should get not ok health check', async () => {
const providerWithBadConfig = new KoraLabsHandleProvider({
policyId: Cardano.PolicyId('50fdcdbfa3154db86a87e4b5697ae30d272e0bbcfa8122efd3e301cb'),
serverUrl: ''
});
const result = await providerWithBadConfig.healthCheck();
expect(result.ok).toEqual(false);
});
});

describe('get policy ids', () => {
test('HandleProvider should get handle policy ids', async () => {
const policyIds = await provider.getPolicyIds();

expect(policyIds.length).toEqual(1);
expect(policyIds).toEqual([config.policyId]);
});
});
});
Loading