From 9feb2125183dbaef3207ac9450e570d232993479 Mon Sep 17 00:00:00 2001 From: pragmaxim Date: Sun, 6 Oct 2024 10:59:01 +0200 Subject: [PATCH] generate wallet per user in lending bot --- test/lending.test.ts | 48 +++++++++++------------ test/transaction.test.ts | 85 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 109 insertions(+), 24 deletions(-) diff --git a/test/lending.test.ts b/test/lending.test.ts index 60ef9443f..56e3aae41 100644 --- a/test/lending.test.ts +++ b/test/lending.test.ts @@ -28,17 +28,16 @@ import { } from '@alephium/web3' import { testNodeWallet } from '@alephium/web3-test' import { PrivateKeyWallet, deriveHDWalletPrivateKeyForGroup } from '@alephium/web3-wallet' -import * as bip39 from 'bip39' class LendingBot { private readonly nodeProvider: NodeProvider // This can be initialized with node url + api key in a real application - private readonly mnemonic: string // This should be stored securely in a real application readonly userGroups: Map + readonly userWallets: Map - constructor(nodeProvider: NodeProvider, mnemonic: string) { + constructor(nodeProvider: NodeProvider) { this.nodeProvider = nodeProvider - this.mnemonic = mnemonic this.userGroups = new Map() + this.userWallets = new Map() } addUser(userId: string): PrivateKeyWallet { @@ -47,7 +46,9 @@ class LendingBot { } const groupNumber = this.userGroups.size + const wallet = PrivateKeyWallet.Random(groupNumber, this.nodeProvider) this.userGroups.set(userId, groupNumber) + this.userWallets.set(userId, wallet) return this.getUserWallet(userId) } @@ -56,9 +57,11 @@ class LendingBot { if (groupNumber === undefined) { throw new Error(`User ${userId} does not exist`) } - - const [privateKey, _] = deriveHDWalletPrivateKeyForGroup(this.mnemonic, groupNumber, 'default') - return new PrivateKeyWallet({ privateKey, nodeProvider: this.nodeProvider }) + const wallet = this.userWallets.get(userId) + if (wallet === undefined) { + throw new Error(`User ${userId} wallet does not exist`) + } + return wallet as PrivateKeyWallet } getUserAddress(userId: string) { @@ -73,16 +76,14 @@ class LendingBot { params, await signer.getPublicKey(params.signerAddress) ) - const signedTxResults = await Promise.all( + return await Promise.all( buildTxResults.map(async (tx) => { - const signature = await signer.signRaw(params.signerAddress, tx.txId) - return { ...tx, signature } + return await signer.signAndSubmitUnsignedTx({ + signerAddress: params.signerAddress, + unsignedTx: tx.unsignedTx + }) }) ) - for (const signedTx of signedTxResults) { - await signer.submitTransaction(signedTx) - } - return signedTxResults } async getUserBalance(userId: string) { @@ -92,15 +93,15 @@ class LendingBot { } async distributeWealth(users: string[], deposit: bigint) { - const testWallet = await testNodeWallet() - const signerAddress = (await testWallet.getSelectedAccount()).address + const signer = await testNodeWallet() + const signerAddress = (await signer.getSelectedAccount()).address const destinations = users.map((user) => ({ address: this.addUser(user).address, attoAlphAmount: deposit })) - await this.signAndSubmitMultiGroupTransferTx(testWallet, { + await this.signAndSubmitMultiGroupTransferTx(signer, { signerAddress, destinations }) @@ -108,16 +109,17 @@ class LendingBot { } async transfer(fromUserId: string, toUserData: [string, number][]) { - const fromUserWallet = this.getUserWallet(fromUserId) + const signer = this.getUserWallet(fromUserId) + const signerAddress = signer.address const destinations = toUserData.map(([user, amount]) => ({ address: this.getUserAddress(user), attoAlphAmount: convertAlphAmountWithDecimals(amount)! })) - await this.signAndSubmitMultiGroupTransferTx(fromUserWallet, { - signerAddress: fromUserWallet.address, - destinations: destinations + await this.signAndSubmitMultiGroupTransferTx(signer, { + signerAddress, + destinations }) } } @@ -134,9 +136,7 @@ async function track(label: string, fn: () => Promise): Promise { describe('lendingbot', function () { it('should work', async function () { - const nodeProvider = new NodeProvider('http://127.0.0.1:22973') - const mnemonic = bip39.generateMnemonic() - const lendingBot = new LendingBot(nodeProvider, mnemonic) + const lendingBot = new LendingBot(new NodeProvider('http://127.0.0.1:22973')) // each user will start with 1 ALPH const users = ['user0', 'user1', 'user2'] diff --git a/test/transaction.test.ts b/test/transaction.test.ts index f0f81a072..a01f6584c 100644 --- a/test/transaction.test.ts +++ b/test/transaction.test.ts @@ -25,6 +25,7 @@ import { PrivateKeyWallet } from '@alephium/web3-wallet' import { ONE_ALPH } from '../packages/web3/src' import { Add, Sub, AddMain } from '../artifacts/ts' import { getSigner } from '@alephium/web3-test' +import { TransactionBuilder } from '@alephium/web3/dist/src/signer/tx-builder' describe('transactions', function () { let signer: PrivateKeyWallet @@ -34,6 +35,90 @@ describe('transactions', function () { signer = await getSigner() }) + it('should build multi-group transfer', async () => { + const nodeProvider = web3.getCurrentNodeProvider() + const signer0 = await getSigner(100n * ONE_ALPH, 0) + const signer1 = await getSigner(0n, 1) + const signer2 = await getSigner(0n, 2) + const signer3 = await getSigner(0n, 3) + const signer4 = await getSigner(0n, 0) + + const transferFrom0to1and2 = await TransactionBuilder.from(nodeProvider).buildMultiGroupTransferTx( + { + signerAddress: signer0.address, + destinations: [signer1, signer2].map((signer) => ({ + address: signer.address, + attoAlphAmount: 2n * ONE_ALPH + })) + }, + signer0.publicKey + ) + + const transferFrom0to1and2Result = await Promise.all( + transferFrom0to1and2.map(async (tx) => { + return await signer.signAndSubmitUnsignedTx({ + signerAddress: signer0.publicKey, + unsignedTx: tx.unsignedTx + }) + }) + ) + + const transferFrom1to3and4 = await TransactionBuilder.from(nodeProvider).buildMultiGroupTransferTx( + { + signerAddress: signer1.address, + destinations: [signer3, signer4].map((signer) => ({ + address: signer.address, + attoAlphAmount: ONE_ALPH + })) + }, + signer1.publicKey + ) + + const transferFrom1to3and4Result = await Promise.all( + transferFrom1to3and4.map(async (tx) => { + return await signer.signAndSubmitUnsignedTx({ + signerAddress: signer1.publicKey, + unsignedTx: tx.unsignedTx + }) + }) + ) + + const transferFrom2to3and4 = await TransactionBuilder.from(nodeProvider).buildMultiGroupTransferTx( + { + signerAddress: signer2.address, + destinations: [signer3, signer4].map((signer) => ({ + address: signer.address, + attoAlphAmount: ONE_ALPH + })) + }, + signer2.publicKey + ) + + const transferFrom2to3and4Result = await Promise.all( + transferFrom2to3and4.map(async (tx) => { + return await signer.signAndSubmitUnsignedTx({ + signerAddress: signer2.publicKey, + unsignedTx: tx.unsignedTx + }) + }) + ) + + const signer1Balance = await nodeProvider.addresses.getAddressesAddressBalance(signer0.address) + const signer2Balance = await nodeProvider.addresses.getAddressesAddressBalance(signer1.address) + const signer3Balance = await nodeProvider.addresses.getAddressesAddressBalance(signer2.address) + + const gasCostTransferFrom0to1and2 = transferFrom0to1and2Result.reduce((sum, item) => sum + BigInt(item.gasAmount) * BigInt(item.gasPrice), BigInt(0)) + const gasCostTransferFrom1to3and4 = transferFrom1to3and4Result.reduce((sum, item) => sum + BigInt(item.gasAmount) * BigInt(item.gasPrice), BigInt(0)) + const gasCostTransferFrom2to3and4 = transferFrom2to3and4Result.reduce((sum, item) => sum + BigInt(item.gasAmount) * BigInt(item.gasPrice), BigInt(0)) + const expectedSigner1Balance = 100n * ONE_ALPH - 10n * ONE_ALPH - gasCostTransferFrom0to1and2 + const expectedSigner2Balance = 10n * ONE_ALPH - 5n * ONE_ALPH - gasCostTransferFrom1to3and4 + const expectedSigner3Balance = 5n * ONE_ALPH + + expect(BigInt(signer1Balance.balance)).toBe(expectedSigner1Balance) + expect(BigInt(signer2Balance.balance)).toBe(expectedSigner2Balance) + expect(BigInt(signer3Balance.balance)).toBe(expectedSigner3Balance) + }) + it('should subscribe transaction status', async () => { const sub = Sub.contract const txParams = await sub.txParamsForDeployment(signer, {