diff --git a/packages/web3-test/src/const.ts b/packages/web3-test/src/const.ts index 715d54908..8130f213e 100644 --- a/packages/web3-test/src/const.ts +++ b/packages/web3-test/src/const.ts @@ -17,6 +17,7 @@ along with the library. If not, see . */ import { NodeProvider, web3 } from '@alephium/web3' +import { PrivateKeyWallet } from '@alephium/web3-wallet' export const testPrivateKeys = [ 'a642942e67258589cd2b1822c631506632db5a12aabcf413604e785300d762a5', @@ -31,6 +32,7 @@ export const testWalletName = 'alephium-web3-test-only-wallet' export const testAddress = '1DrDyTr9RpRsQnDnXo2YRiPzPW4ooHX5LLoqXrqfMrpQH' export const testPrivateKey = testPrivateKeys[0] export const testPassword = 'alph' +export const testPrivateKeyWallet = new PrivateKeyWallet({ privateKey: testPrivateKey }) export async function tryGetDevnetNodeProvider(): Promise { const currentNodeProvider = (() => { diff --git a/packages/web3-test/src/index.ts b/packages/web3-test/src/index.ts index fd5d11478..22ac04523 100644 --- a/packages/web3-test/src/index.ts +++ b/packages/web3-test/src/index.ts @@ -18,7 +18,7 @@ along with the library. If not, see . import { disableContractDebugMessage } from '@alephium/web3' -export { testMnemonic, testWalletName, testAddress, testPrivateKey, testPassword } from './const' +export { testMnemonic, testWalletName, testAddress, testPrivateKey, testPrivateKeyWallet, testPassword } from './const' export { mintToken } from './token' export * from './test-wallet' diff --git a/packages/web3-test/src/test-wallet.ts b/packages/web3-test/src/test-wallet.ts index 0c3c5d0fe..0e7044823 100644 --- a/packages/web3-test/src/test-wallet.ts +++ b/packages/web3-test/src/test-wallet.ts @@ -35,7 +35,7 @@ import { testAddress, testMnemonic, testPassword, - testPrivateKey, + testPrivateKeyWallet, testWalletName, tryGetDevnetNodeProvider } from './const' @@ -94,11 +94,10 @@ export async function getSigner(alphAmount = ONE_ALPH * 100n, group = 0): Promis if (availableBalance < alphAmount) { throw new Error('Not enough balance, please restart the devnet') } - const rootWallet = new PrivateKeyWallet({ privateKey: testPrivateKey }) const wallet = PrivateKeyWallet.Random(group) if (alphAmount > 0n) { const destinations = [{ address: wallet.address, attoAlphAmount: alphAmount }] - await rootWallet.signAndSubmitTransferTx({ signerAddress: testAddress, destinations }) + await testPrivateKeyWallet.signAndSubmitTransferTx({ signerAddress: testAddress, destinations }) } return wallet } catch (error) { diff --git a/packages/web3-wallet/src/privatekey-wallet.ts b/packages/web3-wallet/src/privatekey-wallet.ts index 88ad3f2cb..df68972e0 100644 --- a/packages/web3-wallet/src/privatekey-wallet.ts +++ b/packages/web3-wallet/src/privatekey-wallet.ts @@ -30,8 +30,16 @@ export class PrivateKeyWallet extends SignerProviderSimple { readonly publicKey: string readonly address: string readonly group: number - readonly nodeProvider: NodeProvider - readonly explorerProvider: ExplorerProvider | undefined + readonly _nodeProvider: NodeProvider | undefined + readonly _explorerProvider: ExplorerProvider | undefined + + public get nodeProvider(): NodeProvider { + return this._nodeProvider ?? web3.getCurrentNodeProvider() + } + + public get explorerProvider(): ExplorerProvider | undefined { + return this._explorerProvider ?? web3.getCurrentExplorerProvider() + } protected unsafeGetSelectedAccount(): Promise { return Promise.resolve(this.account) @@ -66,8 +74,8 @@ export class PrivateKeyWallet extends SignerProviderSimple { this.publicKey = publicKeyFromPrivateKey(privateKey, this.keyType) this.address = addressFromPublicKey(this.publicKey, this.keyType) this.group = groupOfAddress(this.address) - this.nodeProvider = nodeProvider ?? web3.getCurrentNodeProvider() - this.explorerProvider = explorerProvider ?? web3.getCurrentExplorerProvider() + this._nodeProvider = nodeProvider + this._explorerProvider = explorerProvider } static Random(targetGroup?: number, nodeProvider?: NodeProvider, keyType?: KeyType): PrivateKeyWallet { diff --git a/test/transaction.test.ts b/test/transaction.test.ts index 65eb03bd6..7083c6f30 100644 --- a/test/transaction.test.ts +++ b/test/transaction.test.ts @@ -17,14 +17,13 @@ along with the library. If not, see . */ import { SignTransferChainedTxParams, subscribeToTxStatus } from '../packages/web3' -import { node } from '../packages/web3' +import { node, ONE_ALPH } from '../packages/web3' import { SubscribeOptions, sleep } from '../packages/web3' import { web3 } from '../packages/web3' import { TxStatus } from '../packages/web3' -import { PrivateKeyWallet } from '@alephium/web3-wallet' -import { ONE_ALPH } from '../packages/web3/src' +import { HDWallet, HDWalletAccount, PrivateKeyWallet, generateMnemonic } from '@alephium/web3-wallet' import { Add, Sub, AddMain, Transact, Deposit } from '../artifacts/ts' -import { getSigner, mintToken } from '@alephium/web3-test' +import { getSigner, mintToken, testPrivateKeyWallet } from '../packages/web3-test' import { TransactionBuilder } from '../packages/web3' import { ALPH_TOKEN_ID } from '../packages/web3' @@ -105,46 +104,49 @@ describe('transactions', function () { expect((await addInstance.fetchState()).fields.result).toBe(3n) }) + async function prepareChainedTxTest(): Promise<[HDWallet, HDWalletAccount, HDWalletAccount, HDWalletAccount]> { + const mnemonic = generateMnemonic() + const wallet = new HDWallet({ mnemonic }) + const address1 = wallet.deriveAndAddNewAccount(1) + const address2 = wallet.deriveAndAddNewAccount(2) + const address3 = wallet.deriveAndAddNewAccount(3) + + await testPrivateKeyWallet.signAndSubmitTransferTx({ + signerAddress: testPrivateKeyWallet.address, + destinations: [{ address: address1.address, attoAlphAmount: 100n * ONE_ALPH }] + }) + + return [wallet, address1, address2, address3] + } + it('should build chained transfer txs across groups', async () => { const nodeProvider = web3.getCurrentNodeProvider() - const signer1 = await getSigner(100n * ONE_ALPH, 1) - const signer2 = await getSigner(0n, 2) - const signer3 = await getSigner(0n, 3) + const [wallet, address1, address2, address3] = await prepareChainedTxTest() const transferFrom1To2: SignTransferChainedTxParams = { - signerAddress: signer1.address, - destinations: [{ address: signer2.address, attoAlphAmount: 10n * ONE_ALPH }], + signerAddress: address1.address, + destinations: [{ address: address2.address, attoAlphAmount: 10n * ONE_ALPH }], type: 'Transfer' } const transferFrom2To3: SignTransferChainedTxParams = { - signerAddress: signer2.address, - destinations: [{ address: signer3.address, attoAlphAmount: 5n * ONE_ALPH }], + signerAddress: address2.address, + destinations: [{ address: address3.address, attoAlphAmount: 5n * ONE_ALPH }], type: 'Transfer' } - await expect(signer2.signAndSubmitTransferTx(transferFrom2To3)).rejects.toThrow( + await expect(wallet.signAndSubmitTransferTx(transferFrom2To3)).rejects.toThrow( `[API Error] - Not enough balance: got 0, expected 5001000000000000000 - Status code: 500` ) - const [transferFrom1To2Result, transferFrom2To3Result] = await TransactionBuilder.from(nodeProvider).buildChainedTx( - [transferFrom1To2, transferFrom2To3], - [signer1.publicKey, signer2.publicKey] - ) - - const signedTransferFrom1To2 = await signer1.signAndSubmitUnsignedTx({ - unsignedTx: transferFrom1To2Result.unsignedTx, - signerAddress: signer1.address - }) - - const signedTransferFrom2To3 = await signer2.signAndSubmitUnsignedTx({ - unsignedTx: transferFrom2To3Result.unsignedTx, - signerAddress: signer2.address - }) + const [signedTransferFrom1To2, signedTransferFrom2To3] = await wallet.signAndSubmitChainedTx([ + transferFrom1To2, + transferFrom2To3 + ]) - const signer1Balance = await nodeProvider.addresses.getAddressesAddressBalance(signer1.address) - const signer2Balance = await nodeProvider.addresses.getAddressesAddressBalance(signer2.address) - const signer3Balance = await nodeProvider.addresses.getAddressesAddressBalance(signer3.address) + const signer1Balance = await nodeProvider.addresses.getAddressesAddressBalance(address1.address) + const signer2Balance = await nodeProvider.addresses.getAddressesAddressBalance(address2.address) + const signer3Balance = await nodeProvider.addresses.getAddressesAddressBalance(address3.address) const gasCostTransferFrom1To2 = BigInt(signedTransferFrom1To2.gasAmount) * BigInt(signedTransferFrom1To2.gasPrice) const gasCostTransferFrom2To3 = BigInt(signedTransferFrom2To3.gasAmount) * BigInt(signedTransferFrom2To3.gasPrice) @@ -159,41 +161,32 @@ describe('transactions', function () { it('should build chain txs that deploy contract in another group', async () => { const nodeProvider = web3.getCurrentNodeProvider() - const signer1 = await getSigner(100n * ONE_ALPH, 1) - const signer2 = await getSigner(0n, 2) + const [wallet, address1, address2] = await prepareChainedTxTest() - const deployTxParams = await Transact.contract.txParamsForDeployment(signer2, { + await wallet.setSelectedAccount(address2.address) + const deployTxParams = await Transact.contract.txParamsForDeployment(wallet, { initialAttoAlphAmount: ONE_ALPH, initialFields: { tokenId: ALPH_TOKEN_ID, totalALPH: 0n, totalTokens: 0n } }) + expect(deployTxParams.signerAddress).toBe(address2.address) - await expect(signer2.signAndSubmitDeployContractTx(deployTxParams)).rejects.toThrow( + await expect(wallet.signAndSubmitDeployContractTx(deployTxParams)).rejects.toThrow( `[API Error] - Insufficient funds for gas` ) const transferTxParams: SignTransferChainedTxParams = { - signerAddress: signer1.address, - destinations: [{ address: signer2.address, attoAlphAmount: 10n * ONE_ALPH }], + signerAddress: address1.address, + destinations: [{ address: address2.address, attoAlphAmount: 10n * ONE_ALPH }], type: 'Transfer' } - const [transferResult, deployResult] = await TransactionBuilder.from(nodeProvider).buildChainedTx( - [transferTxParams, { ...deployTxParams, type: 'DeployContract' }], - [signer1.publicKey, signer2.publicKey] - ) + const [transferResult, deployResult] = await wallet.buildChainedTx([ + transferTxParams, + { ...deployTxParams, type: 'DeployContract' } + ]) - await signer1.signAndSubmitUnsignedTx({ - unsignedTx: transferResult.unsignedTx, - signerAddress: signer1.address - }) - - await signer2.signAndSubmitUnsignedTx({ - unsignedTx: deployResult.unsignedTx, - signerAddress: signer2.address - }) - - const signer1Balance = await nodeProvider.addresses.getAddressesAddressBalance(signer1.address) - const signer2Balance = await nodeProvider.addresses.getAddressesAddressBalance(signer2.address) + const signer1Balance = await nodeProvider.addresses.getAddressesAddressBalance(address1.address) + const signer2Balance = await nodeProvider.addresses.getAddressesAddressBalance(address2.address) const transferTxGasCost = BigInt(transferResult.gasAmount) * BigInt(transferResult.gasPrice) const deployTxGasCost = BigInt(deployResult.gasAmount) * BigInt(deployResult.gasPrice) @@ -211,8 +204,7 @@ describe('transactions', function () { it('should build chain txs that interact with dApp in another group', async () => { const nodeProvider = web3.getCurrentNodeProvider() - const signer1 = await getSigner(100n * ONE_ALPH, 1) - const signer2 = await getSigner(0n, 2) + const [wallet, address1, address2] = await prepareChainedTxTest() // Deploy contract in group 2 const deployer = await getSigner(100n * ONE_ALPH, 2) @@ -224,38 +216,30 @@ describe('transactions', function () { const transactInstance = deploy.contractInstance expect(transactInstance.groupIndex).toBe(2) - const depositTxParams = await Deposit.script.txParamsForExecution(signer2, { + await wallet.setSelectedAccount(address2.address) + const depositTxParams = await Deposit.script.txParamsForExecution(wallet, { initialFields: { c: transactInstance.contractId }, attoAlphAmount: ONE_ALPH }) + expect(depositTxParams.signerAddress).toBe(address2.address) - await expect(signer2.signAndSubmitExecuteScriptTx(depositTxParams)).rejects.toThrow( + await expect(wallet.signAndSubmitExecuteScriptTx(depositTxParams)).rejects.toThrow( `[API Error] - Insufficient funds for gas` ) const transferTxParams: SignTransferChainedTxParams = { - signerAddress: signer1.address, - destinations: [{ address: signer2.address, attoAlphAmount: 10n * ONE_ALPH }], + signerAddress: address1.address, + destinations: [{ address: address2.address, attoAlphAmount: 10n * ONE_ALPH }], type: 'Transfer' } - const [transferResult, depositResult] = await TransactionBuilder.from(nodeProvider).buildChainedTx( - [transferTxParams, { ...depositTxParams, type: 'ExecuteScript' }], - [signer1.publicKey, signer2.publicKey] - ) - - await signer1.signAndSubmitUnsignedTx({ - unsignedTx: transferResult.unsignedTx, - signerAddress: signer1.address - }) - - await signer2.signAndSubmitUnsignedTx({ - unsignedTx: depositResult.unsignedTx, - signerAddress: signer2.address - }) + const [transferResult, depositResult] = await wallet.buildChainedTx([ + transferTxParams, + { ...depositTxParams, type: 'ExecuteScript' } + ]) - const signer1Balance = await nodeProvider.addresses.getAddressesAddressBalance(signer1.address) - const signer2Balance = await nodeProvider.addresses.getAddressesAddressBalance(signer2.address) + const signer1Balance = await nodeProvider.addresses.getAddressesAddressBalance(address1.address) + const signer2Balance = await nodeProvider.addresses.getAddressesAddressBalance(address2.address) const contractBalance = await nodeProvider.addresses.getAddressesAddressBalance(transactInstance.address) const transferTxGasCost = BigInt(transferResult.gasAmount) * BigInt(transferResult.gasPrice)