-
Notifications
You must be signed in to change notification settings - Fork 0
Pectra Hiero tests #71
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
Draft
0x-ds
wants to merge
42
commits into
run-pectra-tests-with-hh
Choose a base branch
from
denys/run-pectra-tests-with-hh
base: run-pectra-tests-with-hh
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Draft
Changes from 17 commits
Commits
Show all changes
42 commits
Select commit
Hold shift + click to select a range
63af181
working version of 'should return delegation designation to 0x167 whe…
0x-ds 17e4f59
working version of should transfer HTS tokens when two EOAs delegate …
0x-ds cebe8bf
remove duped code
0x-ds 9c23899
refactor: move literals to constants
0x-ds 81fd5c9
refactor: move Nonce class out of the test
0x-ds 7543866
refactor: extract helper functions
0x-ds e036165
refactor: remove loop (that ends up in more code)
0x-ds 65ed5cc
final clean up
0x-ds 1098d26
remove redundant logs
0x-ds 8c5eb22
address review comments
0x-ds 80b9233
revert unneeded diff
0x-ds 1761b37
a wroking code assertion for HTS tokne
0x-ds 7a7de21
query HTS token endpoint instead of id derivation hack
0x-ds aa5e441
new tests
0x-ds 82518b6
formatting
0x-ds 9d00367
add new test case
0x-ds 10a2284
Merge branch 'run-pectra-tests-with-hh' of github.com:hashgraph/heder…
0x-ds 547ff2f
update target branch
0x-ds 30e0fdc
manual merge
0x-ds 87835dd
two more tests
0x-ds bb9ea99
new tests
0x-ds 7812c4e
manual merge
0x-ds 16fb79f
fix export isues
0x-ds 9e83e66
rename the test suite
0x-ds f34c7fe
refactor test to perform fields validations modified by proxy calls
0x-ds c35cf00
Merge branch 'run-pectra-tests-with-hh' into denys/run-pectra-tests-w…
0x-ds 6648ae9
remove the demo test
0x-ds 7cb9342
insufficient gas tests
0x-ds 2ce9b51
insufficient gas tests - add TODO
0x-ds 2bcbbe7
Merge branch 'denys/run-pectra-tests-with-hh' of github.com:hashgraph…
0x-ds 2e8ba92
gas verification test
0x-ds 63c3912
add 'should revert transaction delegated to a system contract' test
0x-ds 8d26b32
add 'should no-op transaction delegated to another EOA/HAS facade' test
0x-ds e1375b2
add 'should no-op transaction delegated to another EOA/HAS facade' test
0x-ds 15356d2
skip the test for now
0x-ds fc95881
Merge branch 'run-pectra-tests-with-hh' into denys/run-pectra-tests-w…
0x-ds e3856d7
remove redundant helper
0x-ds a9e638b
manual merge
0x-ds a7efa84
remove waitFor helper usage
0x-ds 7ea66a3
Merge branch 'run-pectra-tests-with-hh' of github.com:hashgraph/heder…
0x-ds 4d543e4
rollback port changes, add separate test target
0x-ds 3057bd5
shard the tests
0x-ds File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,319 @@ | ||
| const assert = require('node:assert').strict; | ||
| const log = require('node:util').debuglog('hip-1340:hts'); | ||
|
|
||
| const {expect} = require('chai'); | ||
| const {ethers} = require('hardhat'); | ||
| const { | ||
| deploy, | ||
| createAndFundEOA, | ||
| waitFor, | ||
| Nonce, | ||
| sendDelegation, | ||
| verifyDelegation, | ||
| designatorFor, | ||
| encodeFunctionData | ||
| } = require('./utils/web3'); | ||
| const { | ||
| associateHtsToken, | ||
| associateHtsTokenViaDelegation, | ||
| transferHtsTokenViaDelegation, | ||
| executeBatchViaDelegation | ||
| } = require('./utils/hts'); | ||
| const {setupProviderAndNetwork} = require('./utils/setup'); | ||
| const Utils = require('../../utils/utils'); | ||
| const {validateErcEvent} = require('../../utils/events'); | ||
| const {HTS_ADDRESS, ONE_HBAR, TINYBAR_TO_WEIBAR_COEF} = require("../../utils/constants"); | ||
| const {getContractByteCode} = require("./utils/sdk"); | ||
| const {MirrorNode} = require("evm-functional-testing/mirror-node"); | ||
|
|
||
| const ERC_20_ABI = [ | ||
| 'function name() view returns (string)', | ||
| 'function symbol() view returns (string)', | ||
| 'function totalSupply() view returns (uint256)', | ||
| 'function balanceOf(address owner) view returns (uint256)', | ||
| ]; | ||
|
|
||
| const SIMPLE_7702_ACCOUNT = '@account-abstraction/contracts/accounts/Simple7702Account'; | ||
| const TEST_TOKEN_NAME = "tokenName"; | ||
| const TEST_TOKEN_SYMBOL = "tokenSymbol"; | ||
|
|
||
| describe('HIP-1340 - EIP-7702 features - hiero specific tests', function () { | ||
| /** @type {ethers.JsonRpcProvider | import('hardhat').HardhatEthersProvider} */ | ||
| let provider; | ||
|
|
||
| /** @type {ethers.Network} */ | ||
| let network; | ||
|
|
||
| before(async function () { | ||
| ({provider, network} = await setupProviderAndNetwork()); | ||
| log('Starting test suite `%s` on network `%s` (chain id %s)', this.test.parent.title, network.name, Number(network.chainId)); | ||
| }); | ||
|
|
||
| it('should return delegation designation to 0x167 when an HTS token is created', async function () { | ||
| // Deploy TokenCreateContract and create HTS fungible token | ||
| const tokenCreateContract = await Utils.deployTokenCreateContract(); | ||
| const tokenAddress = await Utils.createFungibleToken(tokenCreateContract, tokenCreateContract.target); | ||
|
|
||
| // Let's verify the token properties | ||
| const tokenContract = new ethers.Contract(tokenAddress, ERC_20_ABI, provider); | ||
| const name = await tokenContract.name(); | ||
| const symbol = await tokenContract.symbol(); | ||
| const totalSupply = await tokenContract.totalSupply(); | ||
|
|
||
| expect(name).to.be.equal(TEST_TOKEN_NAME); | ||
| expect(symbol).to.be.equal(TEST_TOKEN_SYMBOL); | ||
| expect(totalSupply).to.be.equal(10000000000); | ||
|
|
||
| // Let's transfer 1000 tokens to another address | ||
| const anotherAddress = await createAndFundEOA(); | ||
| await associateHtsToken(anotherAddress, tokenAddress); | ||
| // Grant KYC (required because createFungibleTokenPublic creates the token with a KYC key) | ||
| await waitFor(tokenCreateContract.grantTokenKycPublic(tokenAddress, anotherAddress.address)); | ||
|
|
||
| await waitFor(tokenCreateContract.transferTokenPublic(tokenAddress, anotherAddress.address, 1000)); | ||
| const anotherAddressBalance = await tokenContract.balanceOf(anotherAddress.address); | ||
| expect(anotherAddressBalance).to.be.equal(1000); | ||
|
|
||
| // Verify the HTS token address has a delegation designator pointing to 0x167 | ||
| const {token_id: tokenId} = await new MirrorNode().getToken(tokenAddress); | ||
| const bytecode = await getContractByteCode(tokenId); | ||
| const contractBytecode = '0x' + Buffer.from(bytecode).toString('hex'); | ||
| // TODO(pectra): Reenable check once MN and Relay include support for EIP-7702 | ||
| // const code = await provider.getCode(tokenAddress); | ||
| // expect(code).to.be.equal(designatorFor(HTS_ADDRESS)); | ||
| expect(contractBytecode).to.be.equal(designatorFor(HTS_ADDRESS)); | ||
| }); | ||
|
|
||
| it('should transfer HTS tokens when two EOAs delegate to the same Smart Wallet and send self-sponsored transactions', async function () { | ||
| // Create two EOAs and a receiver, all delegated to the same Smart Wallet | ||
| const eoa1 = await createAndFundEOA(); | ||
| const eoa2 = await createAndFundEOA(); | ||
| const receiver = await createAndFundEOA(); | ||
|
|
||
| // Deploy Simple7702Account (the Smart Wallet both EOAs will delegate to) | ||
| const smartWallet = await deploy(SIMPLE_7702_ACCOUNT); | ||
|
|
||
| const [eoa1Nonce, eoa2Nonce, receiverNonce] = [new Nonce(), new Nonce(), new Nonce()]; | ||
|
|
||
| // Delegate both EOAs to the Smart Wallet and verify via SDK bytecode | ||
| await sendDelegation(eoa1, smartWallet.address, eoa1Nonce); | ||
| await verifyDelegation(eoa1.address, smartWallet.address); | ||
|
|
||
| await sendDelegation(eoa2, smartWallet.address, eoa2Nonce); | ||
| await verifyDelegation(eoa2.address, smartWallet.address); | ||
|
|
||
| // Deploy TokenCreateContract and create HTS fungible token | ||
| const tokenCreateContract = await Utils.deployTokenCreateContract(); | ||
|
|
||
| const tokenAddress = await Utils.createFungibleToken(tokenCreateContract, tokenCreateContract.target); | ||
|
|
||
| // Associate all accounts with the HTS token | ||
| await associateHtsTokenViaDelegation(eoa1, tokenAddress, eoa1Nonce); | ||
| await associateHtsTokenViaDelegation(eoa2, tokenAddress, eoa2Nonce); | ||
| await associateHtsToken(receiver, tokenAddress, receiverNonce); | ||
|
|
||
| // Grant KYC (required because createFungibleTokenPublic creates the token with a KYC key) | ||
| await waitFor(tokenCreateContract.grantTokenKycPublic(tokenAddress, eoa1.address)); | ||
| await waitFor(tokenCreateContract.grantTokenKycPublic(tokenAddress, eoa2.address)); | ||
| await waitFor(tokenCreateContract.grantTokenKycPublic(tokenAddress, receiver.address)); | ||
|
|
||
| // Transfer HTS tokens from treasury to both EOAs | ||
| await waitFor(tokenCreateContract.transferTokenPublic(tokenAddress, eoa1.address, 5_000n)); | ||
| await waitFor(tokenCreateContract.transferTokenPublic(tokenAddress, eoa2.address, 7_000n)); | ||
|
|
||
| // Verify initial balances via ERC20 proxy | ||
| const tokenContract = new ethers.Contract(tokenAddress, ERC_20_ABI, provider); | ||
|
|
||
| const eoa1InitBalance = await tokenContract.balanceOf(eoa1.address); | ||
| assert(eoa1InitBalance === 5_000n, `EOA1 initial balance should be 5000 but got ${eoa1InitBalance}`); | ||
|
|
||
| const eoa2InitBalance = await tokenContract.balanceOf(eoa2.address); | ||
| assert(eoa2InitBalance === 7_000n, `EOA2 initial balance should be 7000 but got ${eoa2InitBalance}`); | ||
|
|
||
| // EOA1 sends self-sponsored transaction to transfer 1500 HTS tokens to receiver | ||
| const receipt1 = await transferHtsTokenViaDelegation(eoa1, tokenAddress, receiver.address, 1_500n, eoa1Nonce); | ||
| assert(receipt1 !== null, 'EOA1 transfer receipt is null'); | ||
|
|
||
| // EOA2 sends self-sponsored transaction to transfer 2300 HTS tokens to receiver | ||
| const receipt2 = await transferHtsTokenViaDelegation(eoa2, tokenAddress, receiver.address, 2_300n, eoa2Nonce); | ||
| assert(receipt2 !== null, 'EOA2 transfer receipt is null'); | ||
|
|
||
| // Verify final balances | ||
| const eoa1FinalBalance = await tokenContract.balanceOf(eoa1.address); | ||
| expect(eoa1FinalBalance).to.be.equal(3_500n, 'EOA1 balance should be 3500 after transfer'); | ||
|
|
||
| const eoa2FinalBalance = await tokenContract.balanceOf(eoa2.address); | ||
| expect(eoa2FinalBalance).to.be.equal(4_700n, 'EOA2 balance should be 4700 after transfer'); | ||
|
|
||
| const receiverBalance = await tokenContract.balanceOf(receiver.address); | ||
| expect(receiverBalance).to.be.equal(3_800n, 'Receiver balance should be 3800 (1500 + 2300)'); | ||
|
|
||
| // Verify HTS Transfer events are emitted correctly and visible from the EOA transactions | ||
| await validateErcEvent(receipt1, [ | ||
| {address: tokenAddress, from: eoa1.address, to: receiver.address, amount: 1_500n}, | ||
| ]); | ||
| await validateErcEvent(receipt2, [ | ||
| {address: tokenAddress, from: eoa2.address, to: receiver.address, amount: 2_300n}, | ||
| ]); | ||
| }); | ||
|
|
||
| it('should batch-transfer HTS tokens to multiple recipients from a delegated EOA via executeBatch', async function () { | ||
| // Create Alice (delegator), Bob and Carol (receivers) | ||
| const alice = await createAndFundEOA(); | ||
| const bob = await createAndFundEOA(); | ||
| const carol = await createAndFundEOA(); | ||
|
|
||
| // Deploy Simple7702Account and delegate Alice | ||
| const smartWallet = await deploy(SIMPLE_7702_ACCOUNT); | ||
| const aliceNonce = new Nonce(); | ||
| const bobNonce = new Nonce(); | ||
| const carolNonce = new Nonce(); | ||
|
|
||
| await sendDelegation(alice, smartWallet.address, aliceNonce); | ||
| await verifyDelegation(alice.address, smartWallet.address); | ||
|
|
||
| // Deploy TokenCreateContract and create HTS fungible token | ||
| const tokenCreateContract = await Utils.deployTokenCreateContract(); | ||
| const tokenAddress = await Utils.createFungibleToken(tokenCreateContract, tokenCreateContract.target); | ||
|
|
||
| // Associate all accounts with the HTS token | ||
| await associateHtsTokenViaDelegation(alice, tokenAddress, aliceNonce); | ||
| await associateHtsToken(bob, tokenAddress, bobNonce); | ||
| await associateHtsToken(carol, tokenAddress, carolNonce); | ||
|
|
||
| // Grant KYC to all | ||
| await waitFor(tokenCreateContract.grantTokenKycPublic(tokenAddress, alice.address)); | ||
| await waitFor(tokenCreateContract.grantTokenKycPublic(tokenAddress, bob.address)); | ||
| await waitFor(tokenCreateContract.grantTokenKycPublic(tokenAddress, carol.address)); | ||
|
|
||
| // Transfer 500 HTS tokens from treasury to Alice | ||
| await waitFor(tokenCreateContract.transferTokenPublic(tokenAddress, alice.address, 500n)); | ||
|
|
||
| // Verify initial balances | ||
| const tokenContract = new ethers.Contract(tokenAddress, ERC_20_ABI, provider); | ||
| const aliceInitTokens = await tokenContract.balanceOf(alice.address); | ||
| assert(aliceInitTokens === 500n, `Alice should have 500 tokens but has ${aliceInitTokens}`); | ||
|
|
||
| // Alice batch-transfers: 100 tokens to Bob + 150 tokens to Carol in one transaction | ||
| const transferToBob = encodeFunctionData('transfer(address to, uint256 value)', [bob.address, 100n]); | ||
| const transferToCarol = encodeFunctionData('transfer(address to, uint256 value)', [carol.address, 150n]); | ||
|
|
||
| const receipt = await executeBatchViaDelegation(alice, [ | ||
| {target: tokenAddress, value: 0n, data: transferToBob}, | ||
| {target: tokenAddress, value: 0n, data: transferToCarol}, | ||
| ], aliceNonce); | ||
| assert(receipt !== null, 'Batch execution receipt is null'); | ||
|
|
||
| // Verify final balances | ||
| const aliceFinalTokens = await tokenContract.balanceOf(alice.address); | ||
| expect(aliceFinalTokens).to.be.equal(250n, 'Alice should have 250 tokens remaining (500 - 100 - 150)'); | ||
|
|
||
| const bobTokens = await tokenContract.balanceOf(bob.address); | ||
| expect(bobTokens).to.be.equal(100n, 'Bob should have received 100 tokens'); | ||
|
|
||
| const carolTokens = await tokenContract.balanceOf(carol.address); | ||
| expect(carolTokens).to.be.equal(150n, 'Carol should have received 150 tokens'); | ||
|
|
||
| // Verify HTS Transfer events | ||
| await validateErcEvent(receipt, [ | ||
| {address: tokenAddress, from: alice.address, to: bob.address, amount: 100n}, | ||
| {address: tokenAddress, from: alice.address, to: carol.address, amount: 150n}, | ||
| ]); | ||
| }); | ||
|
|
||
| async function setupBatchHbarAndHtsTransferScenario() { | ||
| const alice = await createAndFundEOA(); | ||
| const bob = await createAndFundEOA(); | ||
|
|
||
| const smartWallet = await deploy(SIMPLE_7702_ACCOUNT); | ||
| const aliceNonce = new Nonce(); | ||
| const bobNonce = new Nonce(); | ||
|
|
||
| await sendDelegation(alice, smartWallet.address, aliceNonce); | ||
| await verifyDelegation(alice.address, smartWallet.address); | ||
|
|
||
| // Deploy TokenCreateContract and create HTS fungible token | ||
| const tokenCreateContract = await Utils.deployTokenCreateContract(); | ||
| const tokenAddress = await Utils.createFungibleToken(tokenCreateContract, tokenCreateContract.target); | ||
|
|
||
| // Alice is always delegated and associates via delegation. | ||
| await associateHtsTokenViaDelegation(alice, tokenAddress, aliceNonce); | ||
|
|
||
| return {alice, bob, smartWallet, aliceNonce, bobNonce, tokenCreateContract, tokenAddress}; | ||
| } | ||
|
|
||
| async function associateRecipientDirectly({bob, tokenAddress, bobNonce}) { | ||
| await associateHtsToken(bob, tokenAddress, bobNonce); | ||
| } | ||
|
|
||
| async function associateRecipientViaDelegation({bob, smartWallet, tokenAddress, bobNonce}) { | ||
| await sendDelegation(bob, smartWallet.address, bobNonce); | ||
| await verifyDelegation(bob.address, smartWallet.address); | ||
| await associateHtsTokenViaDelegation(bob, tokenAddress, bobNonce); | ||
| } | ||
|
|
||
| async function executeAndAssertBatchHbarAndHtsTransfer({ | ||
| alice, | ||
| bob, | ||
| aliceNonce, | ||
| tokenCreateContract, | ||
| tokenAddress, | ||
| }) { | ||
|
|
||
| // Grant KYC to both | ||
| await waitFor(tokenCreateContract.grantTokenKycPublic(tokenAddress, alice.address)); | ||
| await waitFor(tokenCreateContract.grantTokenKycPublic(tokenAddress, bob.address)); | ||
|
|
||
| // Transfer 500 HTS tokens from treasury to Alice | ||
| await waitFor(tokenCreateContract.transferTokenPublic(tokenAddress, alice.address, 500n)); | ||
|
|
||
| const tokenContract = new ethers.Contract(tokenAddress, ERC_20_ABI, provider); | ||
| const aliceInitTokens = await tokenContract.balanceOf(alice.address); | ||
| assert(aliceInitTokens === 500n, `Alice should have 500 tokens but has ${aliceInitTokens}`); | ||
|
|
||
| const bobBalanceBefore = await provider.getBalance(bob.address); | ||
| const bobTokensBefore = await tokenContract.balanceOf(bob.address); | ||
|
|
||
| // Internal EVM calls use tinybars (8 decimals), not weibars (18 decimals). | ||
| const oneHbarInTinybars = ONE_HBAR / TINYBAR_TO_WEIBAR_COEF; | ||
| const transferCalldata = encodeFunctionData( | ||
| 'transfer(address to, uint256 value)', | ||
| [bob.address, 100n] | ||
| ); | ||
|
|
||
| const receipt = await executeBatchViaDelegation(alice, [ | ||
| {target: bob.address, value: oneHbarInTinybars, data: '0x'}, | ||
| {target: tokenAddress, value: 0n, data: transferCalldata}, | ||
| ], aliceNonce); | ||
| assert(receipt !== null, 'Batch execution receipt is null'); | ||
|
|
||
| // Verify HBAR transfer | ||
| const bobBalanceAfter = await provider.getBalance(bob.address); | ||
| expect(bobBalanceAfter - bobBalanceBefore).to.be.equal(ONE_HBAR, 'Bob should have received 1 HBAR'); | ||
|
|
||
| // Verify HTS token transfer | ||
| const aliceFinalTokens = await tokenContract.balanceOf(alice.address); | ||
| expect(aliceFinalTokens).to.be.equal(400n, 'Alice should have 400 tokens remaining'); | ||
|
|
||
| const bobTokensAfter = await tokenContract.balanceOf(bob.address); | ||
| expect(bobTokensAfter - bobTokensBefore).to.be.equal(100n, 'Bob should have received 100 tokens'); | ||
|
|
||
| // Verify HTS Transfer event | ||
| await validateErcEvent(receipt, [ | ||
| {address: tokenAddress, from: alice.address, to: bob.address, amount: 100n}, | ||
| ]); | ||
| } | ||
|
|
||
| it('should batch-transfer 1 HBAR and 100 HTS tokens from a delegated EOA to another EOA via executeBatch', async function () { | ||
| const scenario = await setupBatchHbarAndHtsTransferScenario(); | ||
| await associateRecipientDirectly(scenario); | ||
| await executeAndAssertBatchHbarAndHtsTransfer(scenario); | ||
| }); | ||
|
|
||
| it('should batch-transfer 1 HBAR and 100 HTS tokens when recipient associates via delegation', async function () { | ||
| const scenario = await setupBatchHbarAndHtsTransferScenario(); | ||
| await associateRecipientViaDelegation(scenario); | ||
| await executeAndAssertBatchHbarAndHtsTransfer(scenario); | ||
| }); | ||
|
|
||
| }); | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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.
Given that
eoa1Nonce, et. all refers to the same nonce (no aliases) we could useNumberinstead, right?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.
I guess, I followed example here https://github.com/hashgraph/hedera-evm-testing/pull/71/changes#diff-f6817b63d43af1bcbe4015dde3b6e7b16529cd882f1d5f3743ec046e8eeec713L152