Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: transaction template #808

Open
wants to merge 29 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
cbb5490
feat: POC implementation
r4mmer Nov 11, 2024
9755aa8
feat: initial implementation
r4mmer Nov 11, 2024
a342ab7
feat: add support for create token transaction
r4mmer Dec 16, 2024
edb3894
feat: add balance management for created token reference
r4mmer Dec 17, 2024
250105d
tests: instruction tests
r4mmer Dec 20, 2024
90dab94
feat: complete instruction
r4mmer Dec 23, 2024
13efa80
feat: complete instruction
r4mmer Dec 23, 2024
fec87f6
feat: executor tests
r4mmer Dec 26, 2024
fff192b
tests: executor and interpreter tests
r4mmer Dec 26, 2024
d46c571
tests(integration): custom token integration template tests
r4mmer Dec 26, 2024
6f91f9e
chore: linter changes
r4mmer Dec 27, 2024
3a3b9ea
chore: linter changes
r4mmer Dec 27, 2024
30f1764
tests(integration): change injectFunds options
r4mmer Dec 27, 2024
3b2ae37
tests(integration): create token tx have different serialization
r4mmer Dec 27, 2024
7099194
tests(integration): include change outputs in checks
r4mmer Dec 27, 2024
edd8234
tests(integration): change output orderr
r4mmer Dec 27, 2024
d120896
tests(integration): add change value to send operation
r4mmer Dec 27, 2024
a50620a
chore: remove helper types to use inferred types from zod
r4mmer Dec 27, 2024
ee5edf4
tests(integration): add change and complete tests
r4mmer Dec 27, 2024
ab0b78e
tests(integration): wrong complete instruction type
r4mmer Dec 30, 2024
49ad6d3
fix: change would add invalid tokens on transaction
r4mmer Dec 31, 2024
674aa2d
tests(unit): mock balance properly
r4mmer Dec 31, 2024
c7e968c
feat: expose transaction template and helper method on the wallet
r4mmer Jan 7, 2025
61b9502
chore: linter changes
r4mmer Jan 8, 2025
ba4110d
test(integration): increase coverage on integration tests
r4mmer Jan 8, 2025
1d8cb3f
tests(integration): change position of tests
r4mmer Jan 8, 2025
b2cd834
tests(integration): change tx version
r4mmer Jan 8, 2025
d82aa19
feat: make tx signing optional when building with the facade
r4mmer Jan 9, 2025
33591c3
feat: correctly pass the arguments to build template
r4mmer Jan 9, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
tests(integration): add change and complete tests
  • Loading branch information
r4mmer committed Dec 27, 2024
commit ee5edf4c5b04e5a72618dded2840cb4add0ce677
44 changes: 44 additions & 0 deletions __tests__/integration/template/transaction/template.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -190,4 +190,48 @@ describe('Template execution', () => {
expect(tx.outputs[1].tokenData).toBe(129);
expect(tx.outputs[1].value).toBe(2n);
});

/* eslint-disable jest/expect-expect */
it('should be able to complete a transaction inputs', async () => {
const address = await hWallet.getAddressAtIndex(25);
const template = new TransactionTemplateBuilder()
.addSetVarAction({ name: 'addr', value: address })
.addSetVarAction({ name: 'token', value: tokenUid })
Comment on lines +197 to +198
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

chore: Not related to these lines, but all tests made here use variable values. At least one of them should test passing the address/value directly to the instructions.

.addSetVarAction({
name: 'tk_balance',
action: 'get_wallet_balance',
options: { token: '{token}' },
})
.addTokenOutput({ address: '{addr}', amount: '{tk_balance}', token: '{token}' })
.addCompleteAction({})
.addShuffleAction({ target: 'all' })
.build();

const tx = await interpreter.build(template, DEBUG);
await transactionUtils.signTransaction(tx, hWallet.storage, DEFAULT_PIN_CODE);
tx.prepareToSend();
const sendTx = new SendTransaction({ storage: hWallet.storage, transaction: tx });
await sendTx.runFromMining();
await waitForTxReceived(hWallet, tx.hash, null);
});

it('should be able to complete change', async () => {
Copy link
Contributor

@tuliomir tuliomir Jan 15, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

chore: The title is not clear about the difference between this test and the last one.

const address = await hWallet.getAddressAtIndex(25);
const template = new TransactionTemplateBuilder()
.addSetVarAction({ name: 'addr', value: address })
.addSetVarAction({ name: 'token', value: tokenUid })
.addUtxoSelect({ fill: 100, token: '{token}' })
.addTokenOutput({ address: '{addr}', amount: 1, token: '{token}' })
.addDataOutput({ data: 'cafe', token: '{token}' })
.addChangeAction({})
.build();

const tx = await interpreter.build(template, DEBUG);
await transactionUtils.signTransaction(tx, hWallet.storage, DEFAULT_PIN_CODE);
tx.prepareToSend();
const sendTx = new SendTransaction({ storage: hWallet.storage, transaction: tx });
await sendTx.runFromMining();
await waitForTxReceived(hWallet, tx.hash, null);
});
/* eslint-enable jest/expect-expect */
});
5 changes: 4 additions & 1 deletion src/template/transaction/interpreter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,15 +41,18 @@
this.txCache = {};
}

async build(instructions: z.infer<typeof TransactionTemplate>, debug: boolean = false): Promise<Transaction> {
async build(
instructions: z.infer<typeof TransactionTemplate>,
debug: boolean = false
): Promise<Transaction> {
const context = new TxTemplateContext(this.wallet.logger, debug);

Check warning on line 48 in src/template/transaction/interpreter.ts

View check run for this annotation

Codecov / codecov/patch

src/template/transaction/interpreter.ts#L48

Added line #L48 was not covered by tests

for (const ins of TransactionTemplate.parse(instructions)) {
await runInstruction(this, context, ins);

Check warning on line 51 in src/template/transaction/interpreter.ts

View check run for this annotation

Codecov / codecov/patch

src/template/transaction/interpreter.ts#L50-L51

Added lines #L50 - L51 were not covered by tests
}

if (context.version === DEFAULT_TX_VERSION) {
return new Transaction(context.inputs, context.outputs, {

Check warning on line 55 in src/template/transaction/interpreter.ts

View check run for this annotation

Codecov / codecov/patch

src/template/transaction/interpreter.ts#L55

Added line #L55 was not covered by tests
signalBits: context.signalBits,
version: context.version,
tokens: context.tokens,
Expand All @@ -57,9 +60,9 @@
}
if (context.version === CREATE_TOKEN_TX_VERSION) {
if (!context.tokenName || !context.tokenSymbol) {
throw new Error('Cannot create a token without a name or symbol');

Check warning on line 63 in src/template/transaction/interpreter.ts

View check run for this annotation

Codecov / codecov/patch

src/template/transaction/interpreter.ts#L63

Added line #L63 was not covered by tests
}
return new CreateTokenTransaction(

Check warning on line 65 in src/template/transaction/interpreter.ts

View check run for this annotation

Codecov / codecov/patch

src/template/transaction/interpreter.ts#L65

Added line #L65 was not covered by tests
context.tokenName,
context.tokenSymbol,
context.inputs,
Expand All @@ -67,7 +70,7 @@
{ signalBits: context.signalBits }
);
}
throw new Error('Unsupported Version byte provided');

Check warning on line 73 in src/template/transaction/interpreter.ts

View check run for this annotation

Codecov / codecov/patch

src/template/transaction/interpreter.ts#L73

Added line #L73 was not covered by tests
}

async getAddress(markAsUsed: boolean = false): Promise<string> {
Expand All @@ -94,65 +97,65 @@

async getUtxos(amount: OutputValueType, options: IGetUtxosOptions): Promise<IGetUtxoResponse> {
// XXX: This may throw, but maybe we should let it.
return this.wallet.getUtxosForAmount(amount, options);

Check warning on line 100 in src/template/transaction/interpreter.ts

View check run for this annotation

Codecov / codecov/patch

src/template/transaction/interpreter.ts#L100

Added line #L100 was not covered by tests
}

async getAuthorities(count: number, options: IGetUtxosOptions): Promise<Utxo[]> {
const newOptions = {

Check warning on line 104 in src/template/transaction/interpreter.ts

View check run for this annotation

Codecov / codecov/patch

src/template/transaction/interpreter.ts#L104

Added line #L104 was not covered by tests
...options,
max_utxos: count,
};
const utxos: Utxo[] = [];

Check warning on line 108 in src/template/transaction/interpreter.ts

View check run for this annotation

Codecov / codecov/patch

src/template/transaction/interpreter.ts#L108

Added line #L108 was not covered by tests
// XXX: This may throw, but maybe we should let it.
for await (const utxo of this.wallet.storage.selectUtxos(newOptions)) {
utxos.push(utxo);

Check warning on line 111 in src/template/transaction/interpreter.ts

View check run for this annotation

Codecov / codecov/patch

src/template/transaction/interpreter.ts#L110-L111

Added lines #L110 - L111 were not covered by tests
}
return utxos;

Check warning on line 113 in src/template/transaction/interpreter.ts

View check run for this annotation

Codecov / codecov/patch

src/template/transaction/interpreter.ts#L113

Added line #L113 was not covered by tests
}

async getTx(txId: string): Promise<IHistoryTx> {
if (this.txCache[txId]) {
return this.txCache[txId];

Check warning on line 118 in src/template/transaction/interpreter.ts

View check run for this annotation

Codecov / codecov/patch

src/template/transaction/interpreter.ts#L118

Added line #L118 was not covered by tests
}

const histtx = await this.wallet.getTx(txId);

Check warning on line 121 in src/template/transaction/interpreter.ts

View check run for this annotation

Codecov / codecov/patch

src/template/transaction/interpreter.ts#L121

Added line #L121 was not covered by tests
if (histtx) {
this.txCache[txId] = histtx as IHistoryTx;
return this.txCache[txId];

Check warning on line 124 in src/template/transaction/interpreter.ts

View check run for this annotation

Codecov / codecov/patch

src/template/transaction/interpreter.ts#L123-L124

Added lines #L123 - L124 were not covered by tests
}

function hidrateIOWithToken<T extends FullNodeInput | FullNodeOutput>(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue: It's confusing to have a function declaration in the middle of running code inside a function.

I'd suggest moving the function declaration to the start or the end of the parent function.

io: T,
tokens: FullNodeToken[]
) {
const { token_data } = io;

Check warning on line 131 in src/template/transaction/interpreter.ts

View check run for this annotation

Codecov / codecov/patch

src/template/transaction/interpreter.ts#L131

Added line #L131 was not covered by tests
if (token_data === 0) {
return {

Check warning on line 133 in src/template/transaction/interpreter.ts

View check run for this annotation

Codecov / codecov/patch

src/template/transaction/interpreter.ts#L133

Added line #L133 was not covered by tests
...io,
token: NATIVE_TOKEN_UID,
};
}

const tokenIdx = tokenUtils.getTokenIndexFromData(token_data);
const tokenUid = tokens[tokenIdx - 1]?.uid;

Check warning on line 140 in src/template/transaction/interpreter.ts

View check run for this annotation

Codecov / codecov/patch

src/template/transaction/interpreter.ts#L139-L140

Added lines #L139 - L140 were not covered by tests
if (!tokenUid) {
throw new Error(`Invalid token_data ${token_data}, token not found in tokens list`);

Check warning on line 142 in src/template/transaction/interpreter.ts

View check run for this annotation

Codecov / codecov/patch

src/template/transaction/interpreter.ts#L142

Added line #L142 was not covered by tests
}

return { ...io, token: tokenUid } as T;

Check warning on line 145 in src/template/transaction/interpreter.ts

View check run for this annotation

Codecov / codecov/patch

src/template/transaction/interpreter.ts#L145

Added line #L145 was not covered by tests
}

const resp = (await this.wallet.getFullTxById(txId)) as FullNodeTxResponse;

Check warning on line 148 in src/template/transaction/interpreter.ts

View check run for this annotation

Codecov / codecov/patch

src/template/transaction/interpreter.ts#L148

Added line #L148 was not covered by tests
// We can assume the wallet handles any network errors
const { tx } = resp;
tx.inputs = tx.inputs.map(i => hidrateIOWithToken<FullNodeInput>(i, tx.tokens));
tx.outputs = tx.outputs.map(o => hidrateIOWithToken<FullNodeOutput>(o, tx.tokens));
const normalizedTx = transactionUtils.convertFullNodeTxToHistoryTx(tx);
this.txCache[txId] = normalizedTx;
return this.txCache[txId];

Check warning on line 155 in src/template/transaction/interpreter.ts

View check run for this annotation

Codecov / codecov/patch

src/template/transaction/interpreter.ts#L150-L155

Added lines #L150 - L155 were not covered by tests
}

getNetwork(): Network {
return this.wallet.getNetworkObject();

Check warning on line 159 in src/template/transaction/interpreter.ts

View check run for this annotation

Codecov / codecov/patch

src/template/transaction/interpreter.ts#L159

Added line #L159 was not covered by tests
}
}
2 changes: 1 addition & 1 deletion src/template/transaction/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@
* LICENSE file in the root directory of this source tree.
*/

import { z } from 'zod';
import { TransactionTemplate } from './instructions';
import { TxTemplateContext } from './context';
import { IHistoryTx, ITokenBalance, ITokenData, OutputValueType } from '../../types';
import Transaction from '../../models/transaction';
import Network from '../../models/network';
import { Utxo } from '../../wallet/types';
import { z } from 'zod';

export interface IGetUtxosOptions {
token?: string;
Expand Down
Loading