Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
6 changes: 6 additions & 0 deletions packages/wallet-service/src/api/txProposalCreate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {
getWallet,
getWalletAddresses,
getWalletAddressDetail,
incrementAddressSeqnum,
markUtxosWithProposalId,
} from '@src/db';
import {
Expand Down Expand Up @@ -137,6 +138,11 @@ export const create = middy(walletIdProxyHandler(async (walletId, event) => {
await markUtxosWithProposalId(mysql, txProposalId, inputUtxos);
}

if (tx.isNanoContract()) {
const nanoHeader = tx.getNanoHeaders()[0];
await incrementAddressSeqnum(mysql, walletId, nanoHeader.address.base58);
}

await commitTransaction(mysql);
} catch (e) {
await rollbackTransaction(mysql);
Expand Down
16 changes: 16 additions & 0 deletions packages/wallet-service/src/db/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -448,6 +448,22 @@ export const getWalletAddressDetail = async (mysql: ServerlessMysql, walletId: s
return null;
};

/**
* Increment the seqnum of an address.
*
* @param mysql - Database connection
* @param walletId - Wallet id
* @param address - Address to increment seqnum for
*/
export const incrementAddressSeqnum = async (mysql: ServerlessMysql, walletId: string, address: string): Promise<void> => {
await mysql.query(`
UPDATE \`address\`
SET \`seqnum\` = \`seqnum\` + 1
WHERE \`wallet_id\` = ?
AND \`address\` = ?`,
[walletId, address]);
};

/**
* Initialize a wallet's transaction history.
*
Expand Down
28 changes: 28 additions & 0 deletions packages/wallet-service/tests/db.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {
getUtxosLockedAtHeight,
getWallet,
getWalletAddressDetail,
incrementAddressSeqnum,
getWalletAddresses,
getWalletTokens,
getWalletBalances,
Expand Down Expand Up @@ -926,6 +927,33 @@ test('getWalletAddressDetail', async () => {
expect(detailNull).toBeNull();
});

test('incrementAddressSeqnum', async () => {
expect.hasAssertions();
const walletId = 'walletId';

await addToAddressTable(mysql, [{
address: ADDRESSES[0],
index: 0,
walletId,
transactions: 0,
seqnum: 5,
}]);

// seqnum should start at 5
const before = await getWalletAddressDetail(mysql, walletId, ADDRESSES[0]);
expect(before.seqnum).toBe(5);

// increment and verify
await incrementAddressSeqnum(mysql, walletId, ADDRESSES[0]);
const after1 = await getWalletAddressDetail(mysql, walletId, ADDRESSES[0]);
expect(after1.seqnum).toBe(6);

// increment again
await incrementAddressSeqnum(mysql, walletId, ADDRESSES[0]);
const after2 = await getWalletAddressDetail(mysql, walletId, ADDRESSES[0]);
expect(after2.seqnum).toBe(7);
});

test('getWalletBalances', async () => {
expect.hasAssertions();
const walletId = 'walletId';
Expand Down
49 changes: 49 additions & 0 deletions packages/wallet-service/tests/txProposal.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { destroy as txProposalDestroy } from '@src/api/txProposalDestroy';
import {
getTxProposal,
getUtxos,
getWalletAddressDetail,
updateTxProposal,
updateVersionData,
} from '@src/db';
Expand Down Expand Up @@ -2097,3 +2098,51 @@ test('markUtxosWithProposalId should handle empty utxos array', async () => {
expect(txProposal).not.toBeNull();
expect(txProposal.status).toBe(TxProposalStatus.OPEN);
});

test('POST /txproposals with nano contract tx should increment caller address seqnum', async () => {
expect.hasAssertions();

await addToWalletTable(mysql, [{
id: 'my-wallet',
xpubkey: 'xpubkey',
authXpubkey: 'auth_xpubkey',
status: 'ready',
maxGap: 5,
createdAt: 10000,
readyAt: 10001,
}]);
await addToAddressTable(mysql, [{
address: ADDRESSES[0],
index: 0,
walletId: 'my-wallet',
transactions: 0,
seqnum: 3,
}]);

// Verify initial seqnum
const before = await getWalletAddressDetail(mysql, 'my-wallet', ADDRESSES[0]);
expect(before.seqnum).toBe(3);

// Mock createTxFromHex to return a nano contract transaction
const spy = jest.spyOn(hathorLib.helpersUtils, 'createTxFromHex').mockReturnValue({
inputs: [],
outputs: [],
isNanoContract: () => true,
getNanoHeaders: () => [{
address: { base58: ADDRESSES[0] },
}],
} as any);

const event = makeGatewayEventWithAuthorizer('my-wallet', null, JSON.stringify({ txHex: 'mockedhex' }));
const result = await txProposalCreate(event, null, null) as APIGatewayProxyResult;
const returnBody = JSON.parse(result.body as string);

expect(result.statusCode).toBe(201);
expect(returnBody.success).toBe(true);

// Verify seqnum was incremented
const after = await getWalletAddressDetail(mysql, 'my-wallet', ADDRESSES[0]);
expect(after.seqnum).toBe(4);

spy.mockRestore();
});