-
Notifications
You must be signed in to change notification settings - Fork 13
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
base: master
Are you sure you want to change the base?
Changes from 1 commit
cbb5490
9755aa8
a342ab7
edb3894
250105d
90dab94
13efa80
fec87f6
fff192b
d46c571
6f91f9e
3a3b9ea
30f1764
3b2ae37
7099194
edd8234
d120896
a50620a
ee5edf4
ab0b78e
49ad6d3
674aa2d
c7e968c
61b9502
ba4110d
1d8cb3f
b2cd834
d82aa19
33591c3
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
- Loading branch information
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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 }) | ||
.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 () => { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 */ | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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); | ||
|
||
for (const ins of TransactionTemplate.parse(instructions)) { | ||
await runInstruction(this, context, ins); | ||
} | ||
|
||
if (context.version === DEFAULT_TX_VERSION) { | ||
return new Transaction(context.inputs, context.outputs, { | ||
signalBits: context.signalBits, | ||
version: context.version, | ||
tokens: context.tokens, | ||
|
@@ -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'); | ||
} | ||
return new CreateTokenTransaction( | ||
context.tokenName, | ||
context.tokenSymbol, | ||
context.inputs, | ||
|
@@ -67,7 +70,7 @@ | |
{ signalBits: context.signalBits } | ||
); | ||
} | ||
throw new Error('Unsupported Version byte provided'); | ||
} | ||
|
||
async getAddress(markAsUsed: boolean = false): Promise<string> { | ||
|
@@ -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); | ||
} | ||
|
||
async getAuthorities(count: number, options: IGetUtxosOptions): Promise<Utxo[]> { | ||
const newOptions = { | ||
...options, | ||
max_utxos: count, | ||
}; | ||
const utxos: Utxo[] = []; | ||
// XXX: This may throw, but maybe we should let it. | ||
for await (const utxo of this.wallet.storage.selectUtxos(newOptions)) { | ||
utxos.push(utxo); | ||
} | ||
return utxos; | ||
} | ||
|
||
async getTx(txId: string): Promise<IHistoryTx> { | ||
if (this.txCache[txId]) { | ||
return this.txCache[txId]; | ||
} | ||
|
||
const histtx = await this.wallet.getTx(txId); | ||
if (histtx) { | ||
this.txCache[txId] = histtx as IHistoryTx; | ||
return this.txCache[txId]; | ||
} | ||
|
||
function hidrateIOWithToken<T extends FullNodeInput | FullNodeOutput>( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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; | ||
if (token_data === 0) { | ||
return { | ||
...io, | ||
token: NATIVE_TOKEN_UID, | ||
}; | ||
} | ||
|
||
const tokenIdx = tokenUtils.getTokenIndexFromData(token_data); | ||
const tokenUid = tokens[tokenIdx - 1]?.uid; | ||
if (!tokenUid) { | ||
throw new Error(`Invalid token_data ${token_data}, token not found in tokens list`); | ||
} | ||
|
||
return { ...io, token: tokenUid } as T; | ||
} | ||
|
||
const resp = (await this.wallet.getFullTxById(txId)) as FullNodeTxResponse; | ||
// 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]; | ||
} | ||
|
||
getNetwork(): Network { | ||
return this.wallet.getNetworkObject(); | ||
} | ||
} |
There was a problem hiding this comment.
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.