-
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 all commits
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
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,263 @@ | ||
import { GenesisWalletHelper } from '../../helpers/genesis-wallet.helper'; | ||
import { | ||
DEFAULT_PIN_CODE, | ||
generateWalletHelper, | ||
stopAllWallets, | ||
waitForTxReceived, | ||
} from '../../helpers/wallet.helper'; | ||
|
||
import HathorWallet from '../../../../src/new/wallet'; | ||
import SendTransaction from '../../../../src/new/sendTransaction'; | ||
import transactionUtils from '../../../../src/utils/transaction'; | ||
import { TransactionTemplateBuilder } from '../../../../src/template/transaction/builder'; | ||
import { WalletTxTemplateInterpreter } from '../../../../src/template/transaction/interpreter'; | ||
|
||
const DEBUG = true; | ||
|
||
describe('Template execution', () => { | ||
let hWallet: HathorWallet; | ||
let interpreter: WalletTxTemplateInterpreter; | ||
let tokenUid: string; | ||
|
||
beforeAll(async () => { | ||
hWallet = await generateWalletHelper(null); | ||
interpreter = new WalletTxTemplateInterpreter(hWallet); | ||
const address = await hWallet.getAddressAtIndex(0); | ||
await GenesisWalletHelper.injectFunds(hWallet, address, 10n, {}); | ||
}); | ||
|
||
afterAll(async () => { | ||
await hWallet.stop(); | ||
await stopAllWallets(); | ||
await GenesisWalletHelper.clearListeners(); | ||
}); | ||
|
||
it('should be able to create a custom token', async () => { | ||
const template = new TransactionTemplateBuilder() | ||
.addConfigAction({ tokenName: 'Tmpl Test Token 01', tokenSymbol: 'TTT01' }) | ||
.addSetVarAction({ name: 'addr', call: { method: 'get_wallet_address' } }) | ||
.addUtxoSelect({ fill: 1 }) | ||
.addTokenOutput({ address: '{addr}', amount: 100, useCreatedToken: true }) | ||
.addAuthorityOutput({ authority: 'mint', address: '{addr}', useCreatedToken: true, count: 5 }) | ||
.addAuthorityOutput({ authority: 'melt', address: '{addr}', useCreatedToken: true, count: 5 }) | ||
.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(); | ||
if (tx.hash === null) { | ||
throw new Error('Transaction does not have a hash'); | ||
} | ||
tokenUid = tx.hash; | ||
await waitForTxReceived(hWallet, tx.hash, null); | ||
|
||
expect(tx.outputs).toHaveLength(12); | ||
|
||
// HTR change | ||
expect(tx.outputs[0].tokenData).toBe(0); | ||
expect(tx.outputs[0].value).toBe(9n); | ||
|
||
// Created token | ||
expect(tx.outputs[1].tokenData).toBe(1); | ||
expect(tx.outputs[1].value).toBe(100n); | ||
|
||
// 5 mint authorities | ||
expect(tx.outputs[2].tokenData).toBe(129); | ||
expect(tx.outputs[2].value).toBe(1n); | ||
expect(tx.outputs[6].tokenData).toBe(129); | ||
expect(tx.outputs[6].value).toBe(1n); | ||
|
||
// 5 melt authorities | ||
expect(tx.outputs[7].tokenData).toBe(129); | ||
expect(tx.outputs[7].value).toBe(2n); | ||
expect(tx.outputs[11].tokenData).toBe(129); | ||
expect(tx.outputs[11].value).toBe(2n); | ||
}); | ||
|
||
it('should be able to send tokens and authorities', async () => { | ||
const address = await hWallet.getAddressAtIndex(10); | ||
const template = new TransactionTemplateBuilder() | ||
.addSetVarAction({ name: 'addr', value: address }) | ||
.addSetVarAction({ name: 'token', value: tokenUid }) | ||
.addUtxoSelect({ fill: 2 }) | ||
.addTokenOutput({ address: '{addr}', amount: 2 }) | ||
.addUtxoSelect({ fill: 3, token: '{token}' }) | ||
.addTokenOutput({ address: '{addr}', amount: 3, token: '{token}' }) | ||
.addAuthoritySelect({ authority: 'mint', token: '{token}', count: 1 }) | ||
.addAuthorityOutput({ address: '{addr}', authority: 'mint', count: 1, token: '{token}' }) | ||
.addAuthoritySelect({ authority: 'melt', token: '{token}', count: 2 }) | ||
.addAuthorityOutput({ address: '{addr}', authority: 'melt', count: 2, token: '{token}' }) | ||
.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); | ||
|
||
expect(tx.outputs).toHaveLength(7); | ||
|
||
// HTR Change | ||
expect(tx.outputs[0].tokenData).toBe(0); | ||
expect(tx.outputs[0].value).toBe(7n); | ||
|
||
// HTR | ||
expect(tx.outputs[1].tokenData).toBe(0); | ||
expect(tx.outputs[1].value).toBe(2n); | ||
|
||
// Custom token change | ||
expect(tx.outputs[2].tokenData).toBe(1); | ||
expect(tx.outputs[2].value).toBe(97n); | ||
|
||
// Custom token | ||
expect(tx.outputs[3].tokenData).toBe(1); | ||
expect(tx.outputs[3].value).toBe(3n); | ||
|
||
// mint authority | ||
expect(tx.outputs[4].tokenData).toBe(129); | ||
expect(tx.outputs[4].value).toBe(1n); | ||
|
||
// melt authorities | ||
expect(tx.outputs[5].tokenData).toBe(129); | ||
expect(tx.outputs[5].value).toBe(2n); | ||
expect(tx.outputs[6].tokenData).toBe(129); | ||
expect(tx.outputs[6].value).toBe(2n); | ||
}); | ||
|
||
it('should be able to destroy authorities', async () => { | ||
const template = new TransactionTemplateBuilder() | ||
.addSetVarAction({ name: 'token', value: tokenUid }) | ||
.addAuthoritySelect({ authority: 'mint', token: '{token}', count: 4 }) | ||
.addAuthoritySelect({ authority: 'melt', token: '{token}', count: 4 }) | ||
.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); | ||
|
||
expect(tx.outputs).toHaveLength(0); | ||
}); | ||
|
||
it('should be able to mint new tokens', async () => { | ||
const address = await hWallet.getAddressAtIndex(15); | ||
const template = new TransactionTemplateBuilder() | ||
.addSetVarAction({ name: 'addr', value: address }) | ||
.addSetVarAction({ name: 'token', value: tokenUid }) | ||
.addUtxoSelect({ fill: 1 }) | ||
.addAuthoritySelect({ authority: 'mint', token: '{token}' }) | ||
.addTokenOutput({ address: '{addr}', amount: 100, token: '{token}' }) | ||
.addAuthorityOutput({ address: '{addr}', authority: 'mint', token: '{token}' }) | ||
.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); | ||
|
||
expect(tx.outputs[0].tokenData).toBe(0); | ||
expect(tx.outputs[1].tokenData).toBe(1); | ||
expect(tx.outputs[1].value).toBe(100n); | ||
}); | ||
|
||
it('should be able to melt tokens', async () => { | ||
const address = await hWallet.getAddressAtIndex(20); | ||
const template = new TransactionTemplateBuilder() | ||
.addSetVarAction({ name: 'addr', value: address }) | ||
.addSetVarAction({ name: 'token', value: tokenUid }) | ||
.addUtxoSelect({ fill: 100, token: '{token}' }) | ||
.addAuthoritySelect({ authority: 'melt', token: '{token}' }) | ||
.addTokenOutput({ address: '{addr}', amount: 1 }) | ||
.addAuthorityOutput({ address: '{addr}', authority: 'melt', token: '{token}' }) | ||
.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); | ||
|
||
expect(tx.outputs[0].tokenData).toBe(0); | ||
expect(tx.outputs[0].value).toBe(1n); | ||
expect(tx.outputs[1].tokenData).toBe(129); | ||
expect(tx.outputs[1].value).toBe(2n); | ||
}); | ||
|
||
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
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: 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', | ||
call: { method: 'get_wallet_balance', token: '{token}' }, | ||
}) | ||
.addTokenOutput({ address: '{addr}', amount: '{tk_balance}', token: '{token}' }) | ||
.addAuthorityOutput({ address: '{addr}', authority: 'mint', token: '{token}' }) | ||
.addAuthorityOutput({ address: '{addr}', authority: 'mint', 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); | ||
expect(tx.hash).toBeDefined(); | ||
}); | ||
|
||
it('should be able to complete a transaction change', 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', | ||
call: { method: 'get_wallet_balance', token: '{token}' }, | ||
}) | ||
.addUtxoSelect({ fill: '{tk_balance}', token: '{token}', autoChange: false }) | ||
.addAuthoritySelect({ token: '{token}', authority: 'mint' }) | ||
.addAuthoritySelect({ token: '{token}', authority: 'melt' }) | ||
.addTokenOutput({ address: '{addr}', amount: 1, token: '{token}' }) | ||
.addCompleteAction({}) | ||
.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); | ||
expect(tx.hash).toBeDefined(); | ||
}); | ||
|
||
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}', autoChange: false }) | ||
.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); | ||
expect(tx.hash).toBeDefined(); | ||
}); | ||
}); |
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.
question: Should we enforce these parameters to be
bigint
s?