Skip to content

Commit

Permalink
feat!: inputResolver resolveInput function now takes an additional pa…
Browse files Browse the repository at this point in the history
…rameter options

BREAKING CHANGE: inputResolver resolveInput function now takes an additional parameter options
  • Loading branch information
AngelCastilloB committed Feb 6, 2024
1 parent c831857 commit 14c486d
Show file tree
Hide file tree
Showing 3 changed files with 183 additions and 21 deletions.
8 changes: 6 additions & 2 deletions packages/core/src/Cardano/Address/PaymentAddress.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {
assertIsBech32WithPrefix,
assertIsHexString
} from '@cardano-sdk/util';
import { HydratedTx, HydratedTxIn, TxIn, TxOut } from '../types';
import { HydratedTx, HydratedTxIn, Tx, TxIn, TxOut } from '../types';
import { NetworkId } from '../ChainId';
import { RewardAccount } from './RewardAccount';

Expand Down Expand Up @@ -68,11 +68,15 @@ export const isAddressWithin =
export const inputsWithAddresses = (tx: HydratedTx, ownAddresses: PaymentAddress[]): HydratedTxIn[] =>
tx.body.inputs.filter(isAddressWithin(ownAddresses));

export type ResolveOptions = {
hints: Tx[];
};

/**
* @param txIn transaction input to resolve associated txOut from
* @returns txOut
*/
export type ResolveInput = (txIn: TxIn) => Promise<TxOut | null>;
export type ResolveInput = (txIn: TxIn, options?: ResolveOptions) => Promise<TxOut | null>;

export interface InputResolver {
resolveInput: ResolveInput;
Expand Down
28 changes: 22 additions & 6 deletions packages/wallet/src/services/WalletUtil.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,20 @@ export interface WalletOutputValidatorContext {
export type WalletUtilContext = WalletOutputValidatorContext & InputResolverContext;

export const createInputResolver = ({ utxo }: InputResolverContext): Cardano.InputResolver => ({
async resolveInput(input: Cardano.TxIn) {
async resolveInput(input: Cardano.TxIn, options?: Cardano.ResolveOptions) {
const utxoAvailable = await firstValueFrom(utxo.available$);
const availableUtxo = utxoAvailable?.find(([txIn]) => txInEquals(txIn, input));
if (!availableUtxo) return null;
return availableUtxo[1];

if (availableUtxo) return availableUtxo[1];

if (options?.hints) {
const tx = options?.hints.find((hint) => hint.id === input.txId);

if (tx && tx.body.outputs.length > input.index) {
return tx.body.outputs[input.index];
}
}
return null;
}
});

Expand Down Expand Up @@ -59,7 +68,14 @@ export const createBackendInputResolver = (provider: ChainHistoryProvider): Card
};

return {
async resolveInput(input: Cardano.TxIn) {
async resolveInput(input: Cardano.TxIn, options?: Cardano.ResolveOptions) {
// Add hints to the cache
if (options?.hints) {
for (const hint of options.hints) {
txCache.set(hint.id, hint);
}
}

const tx = await fetchAndCacheTransaction(input.txId);
if (!tx) return null;

Expand All @@ -74,9 +90,9 @@ export const createBackendInputResolver = (provider: ChainHistoryProvider): Card
* @param resolvers The input resolvers to combine.
*/
export const combineInputResolvers = (...resolvers: Cardano.InputResolver[]): Cardano.InputResolver => ({
async resolveInput(txIn: Cardano.TxIn) {
async resolveInput(txIn: Cardano.TxIn, options?: Cardano.ResolveOptions) {
for (const resolver of resolvers) {
const resolved = await resolver.resolveInput(txIn);
const resolved = await resolver.resolveInput(txIn, options);
if (resolved) return resolved;
}
return null;
Expand Down
168 changes: 155 additions & 13 deletions packages/wallet/test/services/WalletUtil.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,58 @@ describe('WalletUtil', () => {
})
).toBeNull();
});

it('resolveInput resolves inputs from provided hints', async () => {
const tx = {
body: {
outputs: [
{
address: Cardano.PaymentAddress(
'addr_test1qzs0umu0s2ammmpw0hea0w2crtcymdjvvlqngpgqy76gpfnuzcjqw982pcftgx53fu5527z2cj2tkx2h8ux2vxsg475qp3y3vz'
),
value: { coins: 50_000_000n }
},
{
address: Cardano.PaymentAddress(
'addr_test1qzs0umu0s2ammmpw0hea0w2crtcymdjvvlqngpgqy76gpfnuzcjqw982pcftgx53fu5527z2cj2tkx2h8ux2vxsg475qp3y3vz'
),
value: { coins: 150_000_000n }
}
]
},
id: Cardano.TransactionId('0f3abbc8fc19c2e61bab6059bf8a466e6e754833a08a62a6c56fe0e78f19d9d5')
} as Cardano.HydratedTx;

const resolver = createInputResolver({ utxo: { available$: of([]) } });

expect(
await resolver.resolveInput(
{
index: 0,
txId: Cardano.TransactionId('0f3abbc8fc19c2e61bab6059bf8a466e6e754833a08a62a6c56fe0e78f19d9d5')
},
{ hints: [tx] }
)
).toEqual({
address:
'addr_test1qzs0umu0s2ammmpw0hea0w2crtcymdjvvlqngpgqy76gpfnuzcjqw982pcftgx53fu5527z2cj2tkx2h8ux2vxsg475qp3y3vz',
value: { coins: 50_000_000n }
});

expect(
await resolver.resolveInput(
{
index: 1,
txId: Cardano.TransactionId('0f3abbc8fc19c2e61bab6059bf8a466e6e754833a08a62a6c56fe0e78f19d9d5')
},
{ hints: [tx] }
)
).toEqual({
address:
'addr_test1qzs0umu0s2ammmpw0hea0w2crtcymdjvvlqngpgqy76gpfnuzcjqw982pcftgx53fu5527z2cj2tkx2h8ux2vxsg475qp3y3vz',
value: { coins: 150_000_000n }
});
});
});

describe('createBackendInputResolver', () => {
Expand Down Expand Up @@ -117,6 +169,58 @@ describe('WalletUtil', () => {
value: { coins: 150_000_000n }
});
});

it('resolveInput resolves inputs from provided hints', async () => {
const tx = {
body: {
outputs: [
{
address: Cardano.PaymentAddress(
'addr_test1qzs0umu0s2ammmpw0hea0w2crtcymdjvvlqngpgqy76gpfnuzcjqw982pcftgx53fu5527z2cj2tkx2h8ux2vxsg475qp3y3vz'
),
value: { coins: 50_000_000n }
},
{
address: Cardano.PaymentAddress(
'addr_test1qzs0umu0s2ammmpw0hea0w2crtcymdjvvlqngpgqy76gpfnuzcjqw982pcftgx53fu5527z2cj2tkx2h8ux2vxsg475qp3y3vz'
),
value: { coins: 150_000_000n }
}
]
},
id: Cardano.TransactionId('0f3abbc8fc19c2e61bab6059bf8a466e6e754833a08a62a6c56fe0e78f19d9d5')
} as Cardano.HydratedTx;

const resolver = createBackendInputResolver(createMockChainHistoryProvider([]));

expect(
await resolver.resolveInput(
{
index: 0,
txId: Cardano.TransactionId('0f3abbc8fc19c2e61bab6059bf8a466e6e754833a08a62a6c56fe0e78f19d9d5')
},
{ hints: [tx] }
)
).toEqual({
address:
'addr_test1qzs0umu0s2ammmpw0hea0w2crtcymdjvvlqngpgqy76gpfnuzcjqw982pcftgx53fu5527z2cj2tkx2h8ux2vxsg475qp3y3vz',
value: { coins: 50_000_000n }
});

expect(
await resolver.resolveInput(
{
index: 1,
txId: Cardano.TransactionId('0f3abbc8fc19c2e61bab6059bf8a466e6e754833a08a62a6c56fe0e78f19d9d5')
},
{ hints: [tx] }
)
).toEqual({
address:
'addr_test1qzs0umu0s2ammmpw0hea0w2crtcymdjvvlqngpgqy76gpfnuzcjqw982pcftgx53fu5527z2cj2tkx2h8ux2vxsg475qp3y3vz',
value: { coins: 150_000_000n }
});
});
});

describe('combineInputResolvers', () => {
Expand Down Expand Up @@ -206,7 +310,23 @@ describe('WalletUtil', () => {
});
});

it('can resolve inputs from own transactions and from chain history provider', async () => {
it('can resolve inputs from own transactions, hints and from chain history provider', async () => {
const hints = [
{
body: {
outputs: [
{
address: Cardano.PaymentAddress(
'addr_test1qzs0umu0s2ammmpw0hea0w2crtcymdjvvlqngpgqy76gpfnuzcjqw982pcftgx53fu5527z2cj2tkx2h8ux2vxsg475qp3y3vz'
),
value: { coins: 200_000_000n }
}
]
},
id: Cardano.TransactionId('0000bbc8fc19c2e61bab6059bf8a466e6e754833a08a62a6c56fe0FFFFFFFFFF')
} as Cardano.HydratedTx
];

const tx = {
body: {
outputs: [
Expand Down Expand Up @@ -248,36 +368,58 @@ describe('WalletUtil', () => {
);

expect(
await resolver.resolveInput({
index: 0,
txId: Cardano.TransactionId('0f3abbc8fc19c2e61bab6059bf8a466e6e754833a08a62a6c56fe0e78f19d9d5')
})
await resolver.resolveInput(
{
index: 0,
txId: Cardano.TransactionId('0f3abbc8fc19c2e61bab6059bf8a466e6e754833a08a62a6c56fe0e78f19d9d5')
},
{ hints }
)
).toEqual({
address: 'addr_test1vr8nl4u0u6fmtfnawx2rxfz95dy7m46t6dhzdftp2uha87syeufdg',
value: { coins: 50_000_000n }
});

expect(
await resolver.resolveInput({
index: 0,
txId: Cardano.TransactionId('0f3abbc8fc19c2e61bab6059bf8a466e6e754833a08a62a6c56fe0e7FFFFFFFF')
})
await resolver.resolveInput(
{
index: 0,
txId: Cardano.TransactionId('0f3abbc8fc19c2e61bab6059bf8a466e6e754833a08a62a6c56fe0e7FFFFFFFF')
},
{ hints }
)
).toEqual({
address:
'addr_test1qzs0umu0s2ammmpw0hea0w2crtcymdjvvlqngpgqy76gpfnuzcjqw982pcftgx53fu5527z2cj2tkx2h8ux2vxsg475qp3y3vz',
value: { coins: 50_000_000n }
});

expect(
await resolver.resolveInput({
index: 1,
txId: Cardano.TransactionId('0f3abbc8fc19c2e61bab6059bf8a466e6e754833a08a62a6c56fe0e7FFFFFFFF')
})
await resolver.resolveInput(
{
index: 1,
txId: Cardano.TransactionId('0f3abbc8fc19c2e61bab6059bf8a466e6e754833a08a62a6c56fe0e7FFFFFFFF')
},
{ hints }
)
).toEqual({
address:
'addr_test1qzs0umu0s2ammmpw0hea0w2crtcymdjvvlqngpgqy76gpfnuzcjqw982pcftgx53fu5527z2cj2tkx2h8ux2vxsg475qp3y3vz',
value: { coins: 150_000_000n }
});
expect(
await resolver.resolveInput(
{
index: 0,
txId: Cardano.TransactionId('0000bbc8fc19c2e61bab6059bf8a466e6e754833a08a62a6c56fe0FFFFFFFFFF')
},
{ hints }
)
).toEqual({
address:
'addr_test1qzs0umu0s2ammmpw0hea0w2crtcymdjvvlqngpgqy76gpfnuzcjqw982pcftgx53fu5527z2cj2tkx2h8ux2vxsg475qp3y3vz',
value: { coins: 200_000_000n }
});
});

it('resolveInput resolves to null if the input can not be found', async () => {
Expand Down

0 comments on commit 14c486d

Please sign in to comment.