Skip to content

Commit

Permalink
Fix transaction.test.ts; test tokens in chained transactions
Browse files Browse the repository at this point in the history
  • Loading branch information
h0ngcha0 committed Oct 7, 2024
1 parent 975bb16 commit 105d5f2
Show file tree
Hide file tree
Showing 5 changed files with 138 additions and 58 deletions.
16 changes: 11 additions & 5 deletions .project.json
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,13 @@
},
"Deposit": {
"sourceFile": "test/transact.ral",
"sourceCodeHash": "9280bdda186be31318d56d5fb1dfb8153fc8350a83ac37015de0f1cd20dc78cc",
"sourceCodeHash": "732c5dee7df9c1362a8b97ec6308c94e38795d15341b900563b0005cb2c5261c",
"bytecodeDebugPatch": "",
"codeHashDebug": ""
},
"DepositToken": {
"sourceFile": "test/transact.ral",
"sourceCodeHash": "732c5dee7df9c1362a8b97ec6308c94e38795d15341b900563b0005cb2c5261c",
"bytecodeDebugPatch": "",
"codeHashDebug": ""
},
Expand Down Expand Up @@ -222,13 +228,13 @@
},
"MultiDeposit": {
"sourceFile": "test/transact.ral",
"sourceCodeHash": "9280bdda186be31318d56d5fb1dfb8153fc8350a83ac37015de0f1cd20dc78cc",
"sourceCodeHash": "732c5dee7df9c1362a8b97ec6308c94e38795d15341b900563b0005cb2c5261c",
"bytecodeDebugPatch": "",
"codeHashDebug": ""
},
"MultiWithdraw": {
"sourceFile": "test/transact.ral",
"sourceCodeHash": "9280bdda186be31318d56d5fb1dfb8153fc8350a83ac37015de0f1cd20dc78cc",
"sourceCodeHash": "732c5dee7df9c1362a8b97ec6308c94e38795d15341b900563b0005cb2c5261c",
"bytecodeDebugPatch": "",
"codeHashDebug": ""
},
Expand Down Expand Up @@ -312,7 +318,7 @@
},
"Transact": {
"sourceFile": "test/transact.ral",
"sourceCodeHash": "9280bdda186be31318d56d5fb1dfb8153fc8350a83ac37015de0f1cd20dc78cc",
"sourceCodeHash": "732c5dee7df9c1362a8b97ec6308c94e38795d15341b900563b0005cb2c5261c",
"bytecodeDebugPatch": "",
"codeHashDebug": "85e3e9a803741af8e92bd43b1b07cde53f39b86cab0ef1a85bab12b10d691b55"
},
Expand Down Expand Up @@ -342,7 +348,7 @@
},
"Withdraw": {
"sourceFile": "test/transact.ral",
"sourceCodeHash": "9280bdda186be31318d56d5fb1dfb8153fc8350a83ac37015de0f1cd20dc78cc",
"sourceCodeHash": "732c5dee7df9c1362a8b97ec6308c94e38795d15341b900563b0005cb2c5261c",
"bytecodeDebugPatch": "",
"codeHashDebug": ""
},
Expand Down
31 changes: 31 additions & 0 deletions artifacts/test/DepositToken.ral.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
{
"version": "v3.8.1",
"name": "DepositToken",
"bytecodeTemplate": "01010300000009b4{1}{2}a3{2}0d0c{0}0102",
"fieldsSig": {
"names": [
"c",
"tokenId",
"amount"
],
"types": [
"Transact",
"ByteVec",
"U256"
],
"isMutable": [
false,
false,
false
]
},
"functions": [
{
"name": "main",
"paramNames": [],
"paramTypes": [],
"paramIsMutable": [],
"returnTypes": []
}
]
}
10 changes: 10 additions & 0 deletions artifacts/ts/scripts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { default as AddMainScriptJson } from "../add/AddMain.ral.json";
import { default as CallScript0ScriptJson } from "../test/CallScript0.ral.json";
import { default as CallScript1ScriptJson } from "../test/CallScript1.ral.json";
import { default as DepositScriptJson } from "../test/Deposit.ral.json";
import { default as DepositTokenScriptJson } from "../test/DepositToken.ral.json";
import { default as DestroyAddScriptJson } from "../add/DestroyAdd.ral.json";
import { default as GreeterMainScriptJson } from "../greeter/GreeterMain.ral.json";
import { default as InsertIntoMapScriptJson } from "../test/InsertIntoMap.ral.json";
Expand Down Expand Up @@ -64,6 +65,15 @@ export const Deposit = new ExecutableScript<{ c: HexString }>(
getContractByCodeHash
);

export const DepositToken = new ExecutableScript<{
c: HexString;
tokenId: HexString;
amount: bigint;
}>(
Script.fromJson(DepositTokenScriptJson, "", AllStructs),
getContractByCodeHash
);

export const DestroyAdd = new ExecutableScript<{
add: HexString;
caller: Address;
Expand Down
4 changes: 4 additions & 0 deletions contracts/test/transact.ral
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,10 @@ TxScript Deposit(c: Transact) {
c.deposit{callerAddress!() -> ALPH: 1 alph}()
}

TxScript DepositToken(c: Transact, tokenId: ByteVec, amount: U256) {
c.depositToken{callerAddress!() -> tokenId: amount}(amount)
}

TxScript Withdraw(c: Transact) {
c.withdraw()
}
135 changes: 82 additions & 53 deletions test/transaction.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,16 @@ along with the library. If not, see <http://www.gnu.org/licenses/>.
*/

import { SignTransferChainedTxParams, subscribeToTxStatus } from '../packages/web3'
import { node, ONE_ALPH } from '../packages/web3'
import { node, ONE_ALPH, DUST_AMOUNT } from '../packages/web3'
import { SubscribeOptions, sleep } from '../packages/web3'
import { web3 } from '../packages/web3'
import { TxStatus } from '../packages/web3'
import { HDWallet, HDWalletAccount, PrivateKeyWallet, generateMnemonic } from '@alephium/web3-wallet'
import { Add, Sub, AddMain, Transact, Deposit } from '../artifacts/ts'
import { Add, Sub, AddMain, Transact, Deposit, DepositToken } from '../artifacts/ts'
import { getSigner, mintToken, testPrivateKeyWallet } from '../packages/web3-test'
import { TransactionBuilder } from '../packages/web3'
import { ALPH_TOKEN_ID } from '../packages/web3'
import { Balance } from '@alephium/web3/src/api/api-alephium'

describe('transactions', function () {
let signer: PrivateKeyWallet
Expand Down Expand Up @@ -107,150 +108,174 @@ describe('transactions', function () {
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)
const account1 = wallet.deriveAndAddNewAccount(1)
const account2 = wallet.deriveAndAddNewAccount(2)
const account3 = wallet.deriveAndAddNewAccount(3)

await testPrivateKeyWallet.signAndSubmitTransferTx({
signerAddress: testPrivateKeyWallet.address,
destinations: [{ address: address1.address, attoAlphAmount: 100n * ONE_ALPH }]
destinations: [{ address: account1.address, attoAlphAmount: 100n * ONE_ALPH }]
})

return [wallet, address1, address2, address3]
return [wallet, account1, account2, account3]
}

it('should build chained transfer txs across groups', async () => {
const nodeProvider = web3.getCurrentNodeProvider()
const [wallet, address1, address2, address3] = await prepareChainedTxTest()
const [wallet, account1, account2, account3] = await prepareChainedTxTest()

const { tokenId } = await mintToken(account1.address, 10n)

const transferFrom1To2: SignTransferChainedTxParams = {
signerAddress: address1.address,
destinations: [{ address: address2.address, attoAlphAmount: 10n * ONE_ALPH }],
signerAddress: account1.address,
destinations: [
{ address: account2.address, attoAlphAmount: 10n * ONE_ALPH, tokens: [{ id: tokenId, amount: 10n }] }
],
type: 'Transfer'
}

const transferFrom2To3: SignTransferChainedTxParams = {
signerAddress: address2.address,
destinations: [{ address: address3.address, attoAlphAmount: 5n * ONE_ALPH }],
signerAddress: account2.address,
destinations: [
{ address: account3.address, attoAlphAmount: 5n * ONE_ALPH, tokens: [{ id: tokenId, amount: 5n }] }
],
type: 'Transfer'
}

await expect(wallet.signAndSubmitTransferTx(transferFrom2To3)).rejects.toThrow(
`[API Error] - Not enough balance: got 0, expected 5001000000000000000 - Status code: 500`
`[API Error] - Not enough balance: got 0, expected 5 - Status code: 500`
)

const [signedTransferFrom1To2, signedTransferFrom2To3] = await wallet.signAndSubmitChainedTx([
transferFrom1To2,
transferFrom2To3
])

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 account1Balance = await nodeProvider.addresses.getAddressesAddressBalance(account1.address)
const account2Balance = await nodeProvider.addresses.getAddressesAddressBalance(account2.address)
const account3Balance = await nodeProvider.addresses.getAddressesAddressBalance(account3.address)

const gasCostTransferFrom1To2 = BigInt(signedTransferFrom1To2.gasAmount) * BigInt(signedTransferFrom1To2.gasPrice)
const gasCostTransferFrom2To3 = BigInt(signedTransferFrom2To3.gasAmount) * BigInt(signedTransferFrom2To3.gasPrice)
const expectedSigner1Balance = 100n * ONE_ALPH - 10n * ONE_ALPH - gasCostTransferFrom1To2
const expectedSigner2Balance = 10n * ONE_ALPH - 5n * ONE_ALPH - gasCostTransferFrom2To3
const expectedSigner3Balance = 5n * ONE_ALPH

expect(BigInt(signer1Balance.balance)).toBe(expectedSigner1Balance)
expect(BigInt(signer2Balance.balance)).toBe(expectedSigner2Balance)
expect(BigInt(signer3Balance.balance)).toBe(expectedSigner3Balance)
const expectedAccount1AlphBalance = 100n * ONE_ALPH - 10n * ONE_ALPH - gasCostTransferFrom1To2 + DUST_AMOUNT
const expectedAccount2AlphBalance = 10n * ONE_ALPH - 5n * ONE_ALPH - gasCostTransferFrom2To3
const expectedAccount3AlphBalance = 5n * ONE_ALPH

expect(BigInt(account1Balance.balance)).toBe(expectedAccount1AlphBalance)
expect(BigInt(account2Balance.balance)).toBe(expectedAccount2AlphBalance)
expect(BigInt(account3Balance.balance)).toBe(expectedAccount3AlphBalance)
expect(tokenBalance(account1Balance, tokenId)).toBeUndefined()
expect(tokenBalance(account2Balance, tokenId)).toBe('5')
expect(tokenBalance(account3Balance, tokenId)).toBe('5')
})

it('should build chain txs that deploy contract in another group', async () => {
const nodeProvider = web3.getCurrentNodeProvider()
const [wallet, address1, address2] = await prepareChainedTxTest()
const [wallet, account1, account2] = await prepareChainedTxTest()

await wallet.setSelectedAccount(address2.address)
await wallet.setSelectedAccount(account2.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)
expect(deployTxParams.signerAddress).toBe(account2.address)

await expect(wallet.signAndSubmitDeployContractTx(deployTxParams)).rejects.toThrow(
`[API Error] - Insufficient funds for gas`
)

const transferTxParams: SignTransferChainedTxParams = {
signerAddress: address1.address,
destinations: [{ address: address2.address, attoAlphAmount: 10n * ONE_ALPH }],
signerAddress: account1.address,
destinations: [{ address: account2.address, attoAlphAmount: 10n * ONE_ALPH }],
type: 'Transfer'
}

const [transferResult, deployResult] = await wallet.buildChainedTx([
const [transferResult, deployResult] = await wallet.signAndSubmitChainedTx([
transferTxParams,
{ ...deployTxParams, type: 'DeployContract' }
])

const signer1Balance = await nodeProvider.addresses.getAddressesAddressBalance(address1.address)
const signer2Balance = await nodeProvider.addresses.getAddressesAddressBalance(address2.address)
const account1Balance = await nodeProvider.addresses.getAddressesAddressBalance(account1.address)
const account2Balance = await nodeProvider.addresses.getAddressesAddressBalance(account2.address)

const transferTxGasCost = BigInt(transferResult.gasAmount) * BigInt(transferResult.gasPrice)
const deployTxGasCost = BigInt(deployResult.gasAmount) * BigInt(deployResult.gasPrice)
const expectedSigner1Balance = 100n * ONE_ALPH - 10n * ONE_ALPH - transferTxGasCost
const expectedSigner2Balance = 10n * ONE_ALPH - deployTxGasCost - ONE_ALPH
const expectedAccount1Balance = 100n * ONE_ALPH - 10n * ONE_ALPH - transferTxGasCost
const expectedAccount2Balance = 10n * ONE_ALPH - deployTxGasCost - ONE_ALPH

expect(BigInt(signer1Balance.balance)).toBe(expectedSigner1Balance)
expect(BigInt(signer2Balance.balance)).toBe(expectedSigner2Balance)
expect(BigInt(account1Balance.balance)).toBe(expectedAccount1Balance)
expect(BigInt(account2Balance.balance)).toBe(expectedAccount2Balance)

const deployTransaction = await nodeProvider.transactions.getTransactionsDetailsTxid(deployResult.txId)
const contractAddress = deployTransaction.generatedOutputs[0].address
const contractBalance = await nodeProvider.addresses.getAddressesAddressBalance(contractAddress)
expect(BigInt(contractBalance.balance)).toBe(ONE_ALPH)
})

it('should build chain txs that interact with dApp in another group', async () => {
it.only('should build chain txs that interact with dApp in another group', async () => {
const nodeProvider = web3.getCurrentNodeProvider()
const [wallet, address1, address2] = await prepareChainedTxTest()
const [wallet, account1, account2] = await prepareChainedTxTest()

// Deploy contract in group 2
const deployer = await getSigner(100n * ONE_ALPH, 2)
const { tokenId } = await mintToken(deployer.address, 10n * 10n ** 18n)
const { tokenId } = await mintToken(account1.address, 10n)
const deploy = await Transact.deploy(deployer, {
initialAttoAlphAmount: ONE_ALPH,
initialFields: { tokenId, totalALPH: 0n, totalTokens: 0n }
})
const transactInstance = deploy.contractInstance
expect(transactInstance.groupIndex).toBe(2)

await wallet.setSelectedAccount(address2.address)
const depositTxParams = await Deposit.script.txParamsForExecution(wallet, {
await wallet.setSelectedAccount(account2.address)
const depositAlphTxParams = await Deposit.script.txParamsForExecution(wallet, {
initialFields: { c: transactInstance.contractId },
attoAlphAmount: ONE_ALPH
})
expect(depositTxParams.signerAddress).toBe(address2.address)
expect(depositAlphTxParams.signerAddress).toBe(account2.address)
await expect(wallet.signAndSubmitExecuteScriptTx(depositAlphTxParams)).rejects.toThrow(
`[API Error] - Insufficient funds for gas`
)

await expect(wallet.signAndSubmitExecuteScriptTx(depositTxParams)).rejects.toThrow(
const depositTokenTxParams = await DepositToken.script.txParamsForExecution(wallet, {
initialFields: { c: transactInstance.contractId, tokenId, amount: 5n },
attoAlphAmount: DUST_AMOUNT,
tokens: [{ id: tokenId, amount: 5n }]
})
expect(depositTokenTxParams.signerAddress).toBe(account2.address)
await expect(wallet.signAndSubmitExecuteScriptTx(depositTokenTxParams)).rejects.toThrow(
`[API Error] - Insufficient funds for gas`
)

const transferTxParams: SignTransferChainedTxParams = {
signerAddress: address1.address,
destinations: [{ address: address2.address, attoAlphAmount: 10n * ONE_ALPH }],
signerAddress: account1.address,
destinations: [
{ address: account2.address, attoAlphAmount: 10n * ONE_ALPH, tokens: [{ id: tokenId, amount: 5n }] }
],
type: 'Transfer'
}

const [transferResult, depositResult] = await wallet.buildChainedTx([
const [transferResult, depositAlphResult, depositTokenResult] = await wallet.signAndSubmitChainedTx([
transferTxParams,
{ ...depositTxParams, type: 'ExecuteScript' }
{ ...depositAlphTxParams, type: 'ExecuteScript' },
{ ...depositTokenTxParams, type: 'ExecuteScript' }
])

const signer1Balance = await nodeProvider.addresses.getAddressesAddressBalance(address1.address)
const signer2Balance = await nodeProvider.addresses.getAddressesAddressBalance(address2.address)
const account1Balance = await nodeProvider.addresses.getAddressesAddressBalance(account1.address)
const account2Balance = await nodeProvider.addresses.getAddressesAddressBalance(account2.address)
const contractBalance = await nodeProvider.addresses.getAddressesAddressBalance(transactInstance.address)

const transferTxGasCost = BigInt(transferResult.gasAmount) * BigInt(transferResult.gasPrice)
const depositTxGasCost = BigInt(depositResult.gasAmount) * BigInt(depositResult.gasPrice)
const expectedSigner1Balance = 100n * ONE_ALPH - 10n * ONE_ALPH - transferTxGasCost
const expectedSigner2Balance = 10n * ONE_ALPH - depositTxGasCost - ONE_ALPH
const depositAlphTxGasCost = BigInt(depositAlphResult.gasAmount) * BigInt(depositAlphResult.gasPrice)
const depositTokenTxGasCost = BigInt(depositTokenResult.gasAmount) * BigInt(depositTokenResult.gasPrice)
const expectedAccount1AlphBalance = 100n * ONE_ALPH - 10n * ONE_ALPH - transferTxGasCost + DUST_AMOUNT
const expectedAccount2AlphBalance = 10n * ONE_ALPH - depositAlphTxGasCost - depositTokenTxGasCost - ONE_ALPH
const expectedContractBalance = ONE_ALPH * 2n

expect(BigInt(signer1Balance.balance)).toBe(expectedSigner1Balance)
expect(BigInt(signer2Balance.balance)).toBe(expectedSigner2Balance)
expect(BigInt(account1Balance.balance)).toBe(expectedAccount1AlphBalance)
expect(BigInt(account2Balance.balance)).toBe(expectedAccount2AlphBalance)
expect(BigInt(contractBalance.balance)).toBe(expectedContractBalance)
expect(tokenBalance(account1Balance, tokenId)).toBe('5')
expect(tokenBalance(account2Balance, tokenId)).toBeUndefined()

const contractState = await transactInstance.fetchState()
expect(contractState.fields.totalALPH).toEqual(ONE_ALPH)
Expand Down Expand Up @@ -296,4 +321,8 @@ describe('transactions', function () {
)
).rejects.toThrow('Unmatched public key')
})

function tokenBalance(balance: Balance, tokenId: string): string | undefined {
return balance.tokenBalances?.find((t) => t.id === tokenId)?.amount
}
})

0 comments on commit 105d5f2

Please sign in to comment.