Skip to content

Commit

Permalink
fix: refactor ObservableWalletSubmitTx to return a context with Handl…
Browse files Browse the repository at this point in the history
…es and Handles implementation in PersonalWallet
  • Loading branch information
VanessaPC committed Jun 5, 2023
1 parent 863aa52 commit e582d4c
Show file tree
Hide file tree
Showing 7 changed files with 67 additions and 10 deletions.
1 change: 1 addition & 0 deletions packages/util-dev/src/mockProviders/index.ts
Expand Up @@ -6,3 +6,4 @@ export * from './mockAssetProvider';
export * from './mockUtxoProvider';
export * from './mockChainHistoryProvider';
export * from './mockRewardsProvider';
export * from './mockHandleProvider';
20 changes: 20 additions & 0 deletions packages/util-dev/src/mockProviders/mockHandleProvider.ts
@@ -0,0 +1,20 @@
import { Cardano } from '@cardano-sdk/core';

export const resolvedHandle = {
handle: 'alice',
hasDatum: false,
policyId: Cardano.PolicyId('50fdcdbfa3154db86a87e4b5697ae30d272e0bbcfa8122efd3e301cb'),
resolvedAddresses: {
cardano:
'addr_test1qqk4sr4f7vtqzd2w90d5nfu3n59jhhpawyphnek2y7er02nkrezryq3ydtmkg0e7e2jvzg443h0ffzfwd09wpcxy2fuqmcnecd'
},
resolvedAt: {
hash: Cardano.BlockId('10d64cc11e9b20e15b6c46aa7b1fed11246f437e62225655a30ea47bf8cc22d0'),
slot: Cardano.Slot(37_834_496)
}
};

export const mockHandleProvider = () => ({
healthCheck: jest.fn().mockResolvedValue({ ok: true }),
resolveHandles: jest.fn().mockResolvedValue([resolvedHandle])
});
28 changes: 22 additions & 6 deletions packages/wallet/src/PersonalWallet/PersonalWallet.ts
Expand Up @@ -47,6 +47,7 @@ import {
ProviderError,
RewardsProvider,
StakePoolProvider,
SubmitTxArgs,
TxBodyCBOR,
TxCBOR,
TxSubmitProvider,
Expand Down Expand Up @@ -137,19 +138,32 @@ export const DEFAULT_LOOK_AHEAD_SEARCH = 20;
// Ideally we should calculate this based on the activeSlotsCoeff and probability of a single block per epoch.
const BLOCK_SLOT_GAP_MULTIPLIER = 2.5;

const isOutgoingTx = (input: Cardano.Tx | TxCBOR | OutgoingTx): input is OutgoingTx =>
const getSerializedTxCBOR = (input: TxCBOR) => TxCBOR.deserialize(input).body;
const isOutgoingTx = (input: Cardano.Tx | TxCBOR | OutgoingTx | SubmitTxArgs): input is OutgoingTx =>
typeof input === 'object' && 'cbor' in input;
const isTxCBOR = (input: Cardano.Tx | TxCBOR | OutgoingTx): input is TxCBOR => typeof input === 'string';
const processOutgoingTx = (input: Cardano.Tx | TxCBOR | OutgoingTx): OutgoingTx => {
const isTxCBOR = (input: Cardano.Tx | TxCBOR | OutgoingTx | SubmitTxArgs): input is TxCBOR => typeof input === 'string';
const isSubmitTxArgs = (input: Cardano.Tx | TxCBOR | OutgoingTx | SubmitTxArgs): input is SubmitTxArgs =>
typeof input === 'object' && 'context' in input;
const processOutgoingTx = (input: Cardano.Tx | TxCBOR | OutgoingTx | SubmitTxArgs): OutgoingTx => {
// TxCbor
if (isTxCBOR(input)) {
return {
body: TxCBOR.deserialize(input).body,
body: getSerializedTxCBOR(input),
cbor: input,
// Do not re-serialize transaction body to compute transaction id
id: Cardano.TransactionId.fromTxBodyCbor(TxBodyCBOR.fromTxCBOR(input))
};
}
// SubmitTxArgs
if (isSubmitTxArgs(input)) {
return {
body: input.signedTransaction.body,
cbor: TxCBOR.serialize(input.signedTransaction),
context: input?.context?.handles ?? [],
// Do not re-serialize transaction body to compute transaction id
id: input.signedTransaction.id
};
}
// OutgoingTx (resubmitted)
if (isOutgoingTx(input)) {
return input;
Expand Down Expand Up @@ -466,7 +480,7 @@ export class PersonalWallet implements ObservableWallet {
}

async finalizeTx({ tx, ...rest }: FinalizeTxProps, stubSign = false): Promise<Cardano.Tx> {
const { tx: signedTx } = await finalizeTx(
const { signedTransaction: signedTx } = await finalizeTx(
tx,
{ ...rest, ownAddresses: await firstValueFrom(this.addresses$) },
{ inputResolver: this.util, keyAgent: this.keyAgent },
Expand All @@ -480,18 +494,20 @@ export class PersonalWallet implements ObservableWallet {
}

async submitTx(
input: Cardano.Tx | TxCBOR | OutgoingTx,
input: Cardano.Tx | TxCBOR | OutgoingTx | SubmitTxArgs,
{ mightBeAlreadySubmitted }: SubmitTxOptions = {}
): Promise<Cardano.TransactionId> {
const outgoingTx = processOutgoingTx(input);
this.#logger.debug(`Submitting transaction ${outgoingTx.id}`);
this.#newTransactions.submitting$.next(outgoingTx);
try {
await this.txSubmitProvider.submitTx({
...(outgoingTx.context && { context: { handles: outgoingTx.context } }),
signedTransaction: outgoingTx.cbor
});
const { slot: submittedAt } = await firstValueFrom(this.tip$);
this.#logger.debug(`Submitted transaction ${outgoingTx.id} at slot ${submittedAt}`);
this.#logger.debug(outgoingTx);
this.#newTransactions.pending$.next(outgoingTx);
return outgoingTx.id;
} catch (error) {
Expand Down
4 changes: 3 additions & 1 deletion packages/wallet/src/services/TransactionReemitter.ts
Expand Up @@ -170,6 +170,8 @@ export const createTransactionReemitter = ({

return {
failed$,
reemit$: merge(rollbackRetry$, unsubmitted$, reemitUnconfirmed$).pipe(map((tx) => pick(tx, ['cbor', 'body', 'id'])))
reemit$: merge(rollbackRetry$, unsubmitted$, reemitUnconfirmed$).pipe(
map((tx) => pick(tx, ['cbor', 'body', 'id', 'context']))
)
};
};
3 changes: 2 additions & 1 deletion packages/wallet/src/services/types.ts
@@ -1,5 +1,5 @@
import { AsyncKeyAgent, GroupedAddress } from '@cardano-sdk/key-management';
import { Cardano, CardanoNodeErrors, EpochRewards, TxCBOR } from '@cardano-sdk/core';
import { Cardano, CardanoNodeErrors, EpochRewards, TxCBOR, HandleResolution } from '@cardano-sdk/core';

Check warning on line 2 in packages/wallet/src/services/types.ts

View workflow job for this annotation

GitHub Actions / build_and_test (ubuntu-20.04)

Member 'HandleResolution' of the import declaration should be sorted alphabetically

Check warning on line 2 in packages/wallet/src/services/types.ts

View workflow job for this annotation

GitHub Actions / build_and_test (ubuntu-20.04)

Member 'HandleResolution' of the import declaration should be sorted alphabetically
import { Observable } from 'rxjs';

export enum TransactionFailure {
Expand Down Expand Up @@ -64,6 +64,7 @@ export interface OutgoingTx {
cbor: TxCBOR;
body: Cardano.TxBody;
id: Cardano.TransactionId;
context?: HandleResolution[] | [];
}

export interface FailedTx extends OutgoingTx {
Expand Down
4 changes: 2 additions & 2 deletions packages/wallet/src/types.ts
@@ -1,4 +1,4 @@
import { Asset, Cardano, EpochInfo, EraSummary, NetworkInfoProvider, TxCBOR } from '@cardano-sdk/core';
import { Asset, Cardano, EpochInfo, EraSummary, NetworkInfoProvider, SubmitTxArgs, TxCBOR } from '@cardano-sdk/core';
import { BalanceTracker, DelegationTracker, TransactionsTracker, UtxoTracker } from './services';
import { Cip30DataSignature } from '@cardano-sdk/dapp-connector';
import { GroupedAddress, cip8 } from '@cardano-sdk/key-management';
Expand Down Expand Up @@ -73,7 +73,7 @@ export interface ObservableWallet {
/**
* @throws CardanoNodeErrors.TxSubmissionError
*/
submitTx(tx: Cardano.Tx | TxCBOR): Promise<Cardano.TransactionId>;
submitTx(tx: Cardano.Tx | TxCBOR | SubmitTxArgs): Promise<Cardano.TransactionId>;

/**
* Create a TxBuilder from this wallet
Expand Down
17 changes: 17 additions & 0 deletions packages/wallet/test/PersonalWallet/methods.test.ts
Expand Up @@ -57,10 +57,12 @@ describe('PersonalWallet methods', () => {
txSubmitProvider = mocks.mockTxSubmitProvider();
networkInfoProvider = mocks.mockNetworkInfoProvider();
utxoProvider = mocks.mockUtxoProvider();

const assetProvider = mocks.mockAssetProvider();
const stakePoolProvider = createStubStakePoolProvider();
const rewardsProvider = mockRewardsProvider();
const chainHistoryProvider = mockChainHistoryProvider();
const handleProvider = mocks.mockHandleProvider();
const groupedAddress: GroupedAddress = {
accountIndex: 0,
address,
Expand All @@ -83,6 +85,7 @@ describe('PersonalWallet methods', () => {
{
assetProvider,
chainHistoryProvider,
handleProvider,
keyAgent,
logger,
networkInfoProvider,
Expand Down Expand Up @@ -268,6 +271,20 @@ describe('PersonalWallet methods', () => {
await expect(wallet.submitTx(tx, { mightBeAlreadySubmitted: true })).resolves.not.toThrow();
expect(await txPending).toEqual(outgoingTx);
});

it.only('resolves on success when submitting a tx when sending coins to a handle', async () => {
const txBuilder = wallet.createTxBuilder();
const txOutput = await txBuilder.buildOutput().handle('alice').coin(1_000_000n).build();

const txOut = await txBuilder.addOutput(txOutput).build().sign();

const txPending = firstValueFrom(wallet.transactions.outgoing.pending$);
await wallet.submitTx(txOut);

const txPendingResult = await txPending;

expect(txPendingResult.body.outputs[0].address).toEqual(mocks.resolvedHandle.resolvedAddresses.cardano);
});
});
});

Expand Down

0 comments on commit e582d4c

Please sign in to comment.