From 519d86e3eed149ac9166823e939ebc20ba81ff4f Mon Sep 17 00:00:00 2001 From: Simeon Nakov Date: Fri, 17 Jan 2025 16:44:45 +0200 Subject: [PATCH 1/9] feat: added test coverage of CancelAirdrop.sol Signed-off-by: Simeon Nakov --- .../CancelAirdrop.sol/CancelAirdrop.json | 6 +- .../CancelAirdrop.sol/CancelAirdrop.json | 2 +- .../examples/hrc-904/CancelAirdrop.sol | 2 +- test/constants.js | 10 + .../hrc-904/CancelAirdropContract.js | 381 ++++++++++++++++++ .../hedera-token-service/utils.js | 171 ++------ 6 files changed, 432 insertions(+), 140 deletions(-) create mode 100644 test/system-contracts/hedera-token-service/hrc-904/CancelAirdropContract.js diff --git a/artifacts/contracts/system-contracts/hedera-token-service/examples/hrc-904/CancelAirdrop.sol/CancelAirdrop.json b/artifacts/contracts/system-contracts/hedera-token-service/examples/hrc-904/CancelAirdrop.sol/CancelAirdrop.json index 5812f5f5d..892eb8ba8 100644 --- a/artifacts/contracts/system-contracts/hedera-token-service/examples/hrc-904/CancelAirdrop.sol/CancelAirdrop.json +++ b/artifacts/contracts/system-contracts/hedera-token-service/examples/hrc-904/CancelAirdrop.sol/CancelAirdrop.json @@ -74,7 +74,7 @@ "type": "int64[]" } ], - "name": "cancelAirdrops", + "name": "cancelMultipleAirdrops", "outputs": [ { "internalType": "int64", @@ -217,8 +217,8 @@ "type": "function" } ], - "bytecode": "0x608060405234801561000f575f80fd5b50610c4d8061001d5f395ff3fe608060405234801561000f575f80fd5b506004361061006f575f3560e01c8063660297ab1161004d578063660297ab146100d25780638f68e13c146100e55780639b23d3d9146100f8575f80fd5b806304e74b561461007357806315dacbea1461009e578063618dc65e146100b1575b5f80fd5b610086610081366004610742565b61010b565b60405160079190910b81526020015b60405180910390f35b6100866100ac366004610782565b6101ca565b6100c46100bf36600461080f565b6102b9565b6040516100959291906108fc565b6100866100e03660046109c3565b6103d0565b6100866100f3366004610ac1565b610567565b610086610106366004610782565b61060f565b6040805160018082528183019092525f91829190816020015b604080516080810182525f8082526020808301829052928201819052606082015282525f19909201910181610124575050604080516080810182525f606082018190526001600160a01b03898116835288811660208401528716928201929092528251929350918291849161019b5761019b610b12565b60200260200101819052506101af82610653565b9250600783900b6016146101c1575f80fd5b50509392505050565b6040516001600160a01b038581166024830152848116604483015283166064820152608481018290525f908190819061016790630aed65f560e11b9060a4015b60408051601f198184030181529181526020820180516001600160e01b03166001600160e01b03199094169390931790925290516102489190610b26565b5f604051808303815f865af19150503d805f8114610281576040519150601f19603f3d011682016040523d82523d5f602084013e610286565b606091505b5091509150816102975760156102ab565b808060200190518101906102ab9190610b41565b60030b979650505050505050565b5f60605f806101676001600160a01b031663618dc65e60e01b87876040516024016102e5929190610b68565b60408051601f198184030181529181526020820180516001600160e01b03166001600160e01b03199094169390931790925290516103239190610b26565b5f604051808303815f865af19150503d805f811461035c576040519150601f19603f3d011682016040523d82523d5f602084013e610361565b606091505b50915091507f4af4780e06fe8cb9df64b0794fa6f01399af979175bb988e35e0e57e594567bc8282604051610397929190610b89565b60405180910390a1816103ba57601560405180602001604052805f8152506103be565b6016815b60039190910b97909650945050505050565b83515f90818167ffffffffffffffff8111156103ee576103ee6107ca565b60405190808252806020026020018201604052801561043e57816020015b604080516080810182525f8082526020808301829052928201819052606082015282525f1990920191018161040c5790505b5090505f5b8281101561054157604080516080810182525f80825260208201819052918101829052606081019190915288828151811061048057610480610b12565b60209081029190910101516001600160a01b0316815287518890839081106104aa576104aa610b12565b6020908102919091018101516001600160a01b03169082015286518790839081106104d7576104d7610b12565b60209081029190910101516001600160a01b03166040820152855186908390811061050457610504610b12565b602090810291909101015160070b60608201528251819084908490811061052d5761052d610b12565b602090810291909101015250600101610443565b5061054b81610653565b9250600783900b60161461055d575f80fd5b5050949350505050565b6040805160018082528183019092525f91829190816020015b604080516080810182525f8082526020808301829052928201819052606082015282525f19909201910181610580575050604080516080810182526001600160a01b0389811682528881166020830152871691810191909152600785900b6060820152815191925090819083905f906105fb576105fb610b12565b602002602001018190525061054b82610653565b6040516001600160a01b038581166024830152848116604483015283166064820152608481018290525f908190819061016790639b23d3d960e01b9060a40161020a565b5f805f6101676001600160a01b031663012ebcaf60e01b8560405160240161067b9190610ba3565b60408051601f198184030181529181526020820180516001600160e01b03166001600160e01b03199094169390931790925290516106b99190610b26565b5f604051808303815f865af19150503d805f81146106f2576040519150601f19603f3d011682016040523d82523d5f602084013e6106f7565b606091505b50915091508161070857601561071c565b8080602001905181019061071c9190610b41565b60030b949350505050565b80356001600160a01b038116811461073d575f80fd5b919050565b5f805f60608486031215610754575f80fd5b61075d84610727565b925061076b60208501610727565b915061077960408501610727565b90509250925092565b5f805f8060808587031215610795575f80fd5b61079e85610727565b93506107ac60208601610727565b92506107ba60408601610727565b9396929550929360600135925050565b634e487b7160e01b5f52604160045260245ffd5b604051601f8201601f1916810167ffffffffffffffff81118282101715610807576108076107ca565b604052919050565b5f8060408385031215610820575f80fd5b61082983610727565b915060208084013567ffffffffffffffff80821115610846575f80fd5b818601915086601f830112610859575f80fd5b81358181111561086b5761086b6107ca565b61087d601f8201601f191685016107de565b91508082528784828501011115610892575f80fd5b80848401858401375f848284010152508093505050509250929050565b5f5b838110156108c95781810151838201526020016108b1565b50505f910152565b5f81518084526108e88160208601602086016108af565b601f01601f19169290920160200192915050565b828152604060208201525f61091460408301846108d1565b949350505050565b5f67ffffffffffffffff821115610935576109356107ca565b5060051b60200190565b5f82601f83011261094e575f80fd5b8135602061096361095e8361091c565b6107de565b8083825260208201915060208460051b870101935086841115610984575f80fd5b602086015b848110156109a75761099a81610727565b8352918301918301610989565b509695505050505050565b8035600781900b811461073d575f80fd5b5f805f80608085870312156109d6575f80fd5b843567ffffffffffffffff808211156109ed575f80fd5b6109f98883890161093f565b9550602091508187013581811115610a0f575f80fd5b610a1b89828a0161093f565b955050604087013581811115610a2f575f80fd5b610a3b89828a0161093f565b945050606087013581811115610a4f575f80fd5b87019050601f81018813610a61575f80fd5b8035610a6f61095e8261091c565b81815260059190911b8201830190838101908a831115610a8d575f80fd5b928401925b82841015610ab257610aa3846109b2565b82529284019290840190610a92565b979a9699509497505050505050565b5f805f8060808587031215610ad4575f80fd5b610add85610727565b9350610aeb60208601610727565b9250610af960408601610727565b9150610b07606086016109b2565b905092959194509250565b634e487b7160e01b5f52603260045260245ffd5b5f8251610b378184602087016108af565b9190910192915050565b5f60208284031215610b51575f80fd5b81518060030b8114610b61575f80fd5b9392505050565b6001600160a01b0383168152604060208201525f61091460408301846108d1565b8215158152604060208201525f61091460408301846108d1565b602080825282518282018190525f919060409081850190868401855b82811015610c0a57815180516001600160a01b0390811686528782015181168887015286820151168686015260609081015160070b9085015260809093019290850190600101610bbf565b509197965050505050505056fea26469706673582212208ee1de80164e305680d6ab87ca9e98500d69d15dfd487324dd17ffe81fe3c9b964736f6c63430008180033", - "deployedBytecode": "0x608060405234801561000f575f80fd5b506004361061006f575f3560e01c8063660297ab1161004d578063660297ab146100d25780638f68e13c146100e55780639b23d3d9146100f8575f80fd5b806304e74b561461007357806315dacbea1461009e578063618dc65e146100b1575b5f80fd5b610086610081366004610742565b61010b565b60405160079190910b81526020015b60405180910390f35b6100866100ac366004610782565b6101ca565b6100c46100bf36600461080f565b6102b9565b6040516100959291906108fc565b6100866100e03660046109c3565b6103d0565b6100866100f3366004610ac1565b610567565b610086610106366004610782565b61060f565b6040805160018082528183019092525f91829190816020015b604080516080810182525f8082526020808301829052928201819052606082015282525f19909201910181610124575050604080516080810182525f606082018190526001600160a01b03898116835288811660208401528716928201929092528251929350918291849161019b5761019b610b12565b60200260200101819052506101af82610653565b9250600783900b6016146101c1575f80fd5b50509392505050565b6040516001600160a01b038581166024830152848116604483015283166064820152608481018290525f908190819061016790630aed65f560e11b9060a4015b60408051601f198184030181529181526020820180516001600160e01b03166001600160e01b03199094169390931790925290516102489190610b26565b5f604051808303815f865af19150503d805f8114610281576040519150601f19603f3d011682016040523d82523d5f602084013e610286565b606091505b5091509150816102975760156102ab565b808060200190518101906102ab9190610b41565b60030b979650505050505050565b5f60605f806101676001600160a01b031663618dc65e60e01b87876040516024016102e5929190610b68565b60408051601f198184030181529181526020820180516001600160e01b03166001600160e01b03199094169390931790925290516103239190610b26565b5f604051808303815f865af19150503d805f811461035c576040519150601f19603f3d011682016040523d82523d5f602084013e610361565b606091505b50915091507f4af4780e06fe8cb9df64b0794fa6f01399af979175bb988e35e0e57e594567bc8282604051610397929190610b89565b60405180910390a1816103ba57601560405180602001604052805f8152506103be565b6016815b60039190910b97909650945050505050565b83515f90818167ffffffffffffffff8111156103ee576103ee6107ca565b60405190808252806020026020018201604052801561043e57816020015b604080516080810182525f8082526020808301829052928201819052606082015282525f1990920191018161040c5790505b5090505f5b8281101561054157604080516080810182525f80825260208201819052918101829052606081019190915288828151811061048057610480610b12565b60209081029190910101516001600160a01b0316815287518890839081106104aa576104aa610b12565b6020908102919091018101516001600160a01b03169082015286518790839081106104d7576104d7610b12565b60209081029190910101516001600160a01b03166040820152855186908390811061050457610504610b12565b602090810291909101015160070b60608201528251819084908490811061052d5761052d610b12565b602090810291909101015250600101610443565b5061054b81610653565b9250600783900b60161461055d575f80fd5b5050949350505050565b6040805160018082528183019092525f91829190816020015b604080516080810182525f8082526020808301829052928201819052606082015282525f19909201910181610580575050604080516080810182526001600160a01b0389811682528881166020830152871691810191909152600785900b6060820152815191925090819083905f906105fb576105fb610b12565b602002602001018190525061054b82610653565b6040516001600160a01b038581166024830152848116604483015283166064820152608481018290525f908190819061016790639b23d3d960e01b9060a40161020a565b5f805f6101676001600160a01b031663012ebcaf60e01b8560405160240161067b9190610ba3565b60408051601f198184030181529181526020820180516001600160e01b03166001600160e01b03199094169390931790925290516106b99190610b26565b5f604051808303815f865af19150503d805f81146106f2576040519150601f19603f3d011682016040523d82523d5f602084013e6106f7565b606091505b50915091508161070857601561071c565b8080602001905181019061071c9190610b41565b60030b949350505050565b80356001600160a01b038116811461073d575f80fd5b919050565b5f805f60608486031215610754575f80fd5b61075d84610727565b925061076b60208501610727565b915061077960408501610727565b90509250925092565b5f805f8060808587031215610795575f80fd5b61079e85610727565b93506107ac60208601610727565b92506107ba60408601610727565b9396929550929360600135925050565b634e487b7160e01b5f52604160045260245ffd5b604051601f8201601f1916810167ffffffffffffffff81118282101715610807576108076107ca565b604052919050565b5f8060408385031215610820575f80fd5b61082983610727565b915060208084013567ffffffffffffffff80821115610846575f80fd5b818601915086601f830112610859575f80fd5b81358181111561086b5761086b6107ca565b61087d601f8201601f191685016107de565b91508082528784828501011115610892575f80fd5b80848401858401375f848284010152508093505050509250929050565b5f5b838110156108c95781810151838201526020016108b1565b50505f910152565b5f81518084526108e88160208601602086016108af565b601f01601f19169290920160200192915050565b828152604060208201525f61091460408301846108d1565b949350505050565b5f67ffffffffffffffff821115610935576109356107ca565b5060051b60200190565b5f82601f83011261094e575f80fd5b8135602061096361095e8361091c565b6107de565b8083825260208201915060208460051b870101935086841115610984575f80fd5b602086015b848110156109a75761099a81610727565b8352918301918301610989565b509695505050505050565b8035600781900b811461073d575f80fd5b5f805f80608085870312156109d6575f80fd5b843567ffffffffffffffff808211156109ed575f80fd5b6109f98883890161093f565b9550602091508187013581811115610a0f575f80fd5b610a1b89828a0161093f565b955050604087013581811115610a2f575f80fd5b610a3b89828a0161093f565b945050606087013581811115610a4f575f80fd5b87019050601f81018813610a61575f80fd5b8035610a6f61095e8261091c565b81815260059190911b8201830190838101908a831115610a8d575f80fd5b928401925b82841015610ab257610aa3846109b2565b82529284019290840190610a92565b979a9699509497505050505050565b5f805f8060808587031215610ad4575f80fd5b610add85610727565b9350610aeb60208601610727565b9250610af960408601610727565b9150610b07606086016109b2565b905092959194509250565b634e487b7160e01b5f52603260045260245ffd5b5f8251610b378184602087016108af565b9190910192915050565b5f60208284031215610b51575f80fd5b81518060030b8114610b61575f80fd5b9392505050565b6001600160a01b0383168152604060208201525f61091460408301846108d1565b8215158152604060208201525f61091460408301846108d1565b602080825282518282018190525f919060409081850190868401855b82811015610c0a57815180516001600160a01b0390811686528782015181168887015286820151168686015260609081015160070b9085015260809093019290850190600101610bbf565b509197965050505050505056fea26469706673582212208ee1de80164e305680d6ab87ca9e98500d69d15dfd487324dd17ffe81fe3c9b964736f6c63430008180033", + "bytecode": "0x608060405234801561000f575f80fd5b50610c4d8061001d5f395ff3fe608060405234801561000f575f80fd5b506004361061006f575f3560e01c80637e5fb7dc1161004d5780637e5fb7dc146100d25780638f68e13c146100e55780639b23d3d9146100f8575f80fd5b806304e74b561461007357806315dacbea1461009e578063618dc65e146100b1575b5f80fd5b610086610081366004610742565b61010b565b60405160079190910b81526020015b60405180910390f35b6100866100ac366004610782565b6101ca565b6100c46100bf36600461080f565b6102b9565b6040516100959291906108fc565b6100866100e03660046109c3565b6103d0565b6100866100f3366004610ac1565b610567565b610086610106366004610782565b61060f565b6040805160018082528183019092525f91829190816020015b604080516080810182525f8082526020808301829052928201819052606082015282525f19909201910181610124575050604080516080810182525f606082018190526001600160a01b03898116835288811660208401528716928201929092528251929350918291849161019b5761019b610b12565b60200260200101819052506101af82610653565b9250600783900b6016146101c1575f80fd5b50509392505050565b6040516001600160a01b038581166024830152848116604483015283166064820152608481018290525f908190819061016790630aed65f560e11b9060a4015b60408051601f198184030181529181526020820180516001600160e01b03166001600160e01b03199094169390931790925290516102489190610b26565b5f604051808303815f865af19150503d805f8114610281576040519150601f19603f3d011682016040523d82523d5f602084013e610286565b606091505b5091509150816102975760156102ab565b808060200190518101906102ab9190610b41565b60030b979650505050505050565b5f60605f806101676001600160a01b031663618dc65e60e01b87876040516024016102e5929190610b68565b60408051601f198184030181529181526020820180516001600160e01b03166001600160e01b03199094169390931790925290516103239190610b26565b5f604051808303815f865af19150503d805f811461035c576040519150601f19603f3d011682016040523d82523d5f602084013e610361565b606091505b50915091507f4af4780e06fe8cb9df64b0794fa6f01399af979175bb988e35e0e57e594567bc8282604051610397929190610b89565b60405180910390a1816103ba57601560405180602001604052805f8152506103be565b6016815b60039190910b97909650945050505050565b83515f90818167ffffffffffffffff8111156103ee576103ee6107ca565b60405190808252806020026020018201604052801561043e57816020015b604080516080810182525f8082526020808301829052928201819052606082015282525f1990920191018161040c5790505b5090505f5b8281101561054157604080516080810182525f80825260208201819052918101829052606081019190915288828151811061048057610480610b12565b60209081029190910101516001600160a01b0316815287518890839081106104aa576104aa610b12565b6020908102919091018101516001600160a01b03169082015286518790839081106104d7576104d7610b12565b60209081029190910101516001600160a01b03166040820152855186908390811061050457610504610b12565b602090810291909101015160070b60608201528251819084908490811061052d5761052d610b12565b602090810291909101015250600101610443565b5061054b81610653565b9250600783900b60161461055d575f80fd5b5050949350505050565b6040805160018082528183019092525f91829190816020015b604080516080810182525f8082526020808301829052928201819052606082015282525f19909201910181610580575050604080516080810182526001600160a01b0389811682528881166020830152871691810191909152600785900b6060820152815191925090819083905f906105fb576105fb610b12565b602002602001018190525061054b82610653565b6040516001600160a01b038581166024830152848116604483015283166064820152608481018290525f908190819061016790639b23d3d960e01b9060a40161020a565b5f805f6101676001600160a01b031663012ebcaf60e01b8560405160240161067b9190610ba3565b60408051601f198184030181529181526020820180516001600160e01b03166001600160e01b03199094169390931790925290516106b99190610b26565b5f604051808303815f865af19150503d805f81146106f2576040519150601f19603f3d011682016040523d82523d5f602084013e6106f7565b606091505b50915091508161070857601561071c565b8080602001905181019061071c9190610b41565b60030b949350505050565b80356001600160a01b038116811461073d575f80fd5b919050565b5f805f60608486031215610754575f80fd5b61075d84610727565b925061076b60208501610727565b915061077960408501610727565b90509250925092565b5f805f8060808587031215610795575f80fd5b61079e85610727565b93506107ac60208601610727565b92506107ba60408601610727565b9396929550929360600135925050565b634e487b7160e01b5f52604160045260245ffd5b604051601f8201601f1916810167ffffffffffffffff81118282101715610807576108076107ca565b604052919050565b5f8060408385031215610820575f80fd5b61082983610727565b915060208084013567ffffffffffffffff80821115610846575f80fd5b818601915086601f830112610859575f80fd5b81358181111561086b5761086b6107ca565b61087d601f8201601f191685016107de565b91508082528784828501011115610892575f80fd5b80848401858401375f848284010152508093505050509250929050565b5f5b838110156108c95781810151838201526020016108b1565b50505f910152565b5f81518084526108e88160208601602086016108af565b601f01601f19169290920160200192915050565b828152604060208201525f61091460408301846108d1565b949350505050565b5f67ffffffffffffffff821115610935576109356107ca565b5060051b60200190565b5f82601f83011261094e575f80fd5b8135602061096361095e8361091c565b6107de565b8083825260208201915060208460051b870101935086841115610984575f80fd5b602086015b848110156109a75761099a81610727565b8352918301918301610989565b509695505050505050565b8035600781900b811461073d575f80fd5b5f805f80608085870312156109d6575f80fd5b843567ffffffffffffffff808211156109ed575f80fd5b6109f98883890161093f565b9550602091508187013581811115610a0f575f80fd5b610a1b89828a0161093f565b955050604087013581811115610a2f575f80fd5b610a3b89828a0161093f565b945050606087013581811115610a4f575f80fd5b87019050601f81018813610a61575f80fd5b8035610a6f61095e8261091c565b81815260059190911b8201830190838101908a831115610a8d575f80fd5b928401925b82841015610ab257610aa3846109b2565b82529284019290840190610a92565b979a9699509497505050505050565b5f805f8060808587031215610ad4575f80fd5b610add85610727565b9350610aeb60208601610727565b9250610af960408601610727565b9150610b07606086016109b2565b905092959194509250565b634e487b7160e01b5f52603260045260245ffd5b5f8251610b378184602087016108af565b9190910192915050565b5f60208284031215610b51575f80fd5b81518060030b8114610b61575f80fd5b9392505050565b6001600160a01b0383168152604060208201525f61091460408301846108d1565b8215158152604060208201525f61091460408301846108d1565b602080825282518282018190525f919060409081850190868401855b82811015610c0a57815180516001600160a01b0390811686528782015181168887015286820151168686015260609081015160070b9085015260809093019290850190600101610bbf565b509197965050505050505056fea2646970667358221220f1a1f12b097858c4cc8e88d72e780efebeaca5729e9bf087780b4700756d2a9764736f6c63430008180033", + "deployedBytecode": "0x608060405234801561000f575f80fd5b506004361061006f575f3560e01c80637e5fb7dc1161004d5780637e5fb7dc146100d25780638f68e13c146100e55780639b23d3d9146100f8575f80fd5b806304e74b561461007357806315dacbea1461009e578063618dc65e146100b1575b5f80fd5b610086610081366004610742565b61010b565b60405160079190910b81526020015b60405180910390f35b6100866100ac366004610782565b6101ca565b6100c46100bf36600461080f565b6102b9565b6040516100959291906108fc565b6100866100e03660046109c3565b6103d0565b6100866100f3366004610ac1565b610567565b610086610106366004610782565b61060f565b6040805160018082528183019092525f91829190816020015b604080516080810182525f8082526020808301829052928201819052606082015282525f19909201910181610124575050604080516080810182525f606082018190526001600160a01b03898116835288811660208401528716928201929092528251929350918291849161019b5761019b610b12565b60200260200101819052506101af82610653565b9250600783900b6016146101c1575f80fd5b50509392505050565b6040516001600160a01b038581166024830152848116604483015283166064820152608481018290525f908190819061016790630aed65f560e11b9060a4015b60408051601f198184030181529181526020820180516001600160e01b03166001600160e01b03199094169390931790925290516102489190610b26565b5f604051808303815f865af19150503d805f8114610281576040519150601f19603f3d011682016040523d82523d5f602084013e610286565b606091505b5091509150816102975760156102ab565b808060200190518101906102ab9190610b41565b60030b979650505050505050565b5f60605f806101676001600160a01b031663618dc65e60e01b87876040516024016102e5929190610b68565b60408051601f198184030181529181526020820180516001600160e01b03166001600160e01b03199094169390931790925290516103239190610b26565b5f604051808303815f865af19150503d805f811461035c576040519150601f19603f3d011682016040523d82523d5f602084013e610361565b606091505b50915091507f4af4780e06fe8cb9df64b0794fa6f01399af979175bb988e35e0e57e594567bc8282604051610397929190610b89565b60405180910390a1816103ba57601560405180602001604052805f8152506103be565b6016815b60039190910b97909650945050505050565b83515f90818167ffffffffffffffff8111156103ee576103ee6107ca565b60405190808252806020026020018201604052801561043e57816020015b604080516080810182525f8082526020808301829052928201819052606082015282525f1990920191018161040c5790505b5090505f5b8281101561054157604080516080810182525f80825260208201819052918101829052606081019190915288828151811061048057610480610b12565b60209081029190910101516001600160a01b0316815287518890839081106104aa576104aa610b12565b6020908102919091018101516001600160a01b03169082015286518790839081106104d7576104d7610b12565b60209081029190910101516001600160a01b03166040820152855186908390811061050457610504610b12565b602090810291909101015160070b60608201528251819084908490811061052d5761052d610b12565b602090810291909101015250600101610443565b5061054b81610653565b9250600783900b60161461055d575f80fd5b5050949350505050565b6040805160018082528183019092525f91829190816020015b604080516080810182525f8082526020808301829052928201819052606082015282525f19909201910181610580575050604080516080810182526001600160a01b0389811682528881166020830152871691810191909152600785900b6060820152815191925090819083905f906105fb576105fb610b12565b602002602001018190525061054b82610653565b6040516001600160a01b038581166024830152848116604483015283166064820152608481018290525f908190819061016790639b23d3d960e01b9060a40161020a565b5f805f6101676001600160a01b031663012ebcaf60e01b8560405160240161067b9190610ba3565b60408051601f198184030181529181526020820180516001600160e01b03166001600160e01b03199094169390931790925290516106b99190610b26565b5f604051808303815f865af19150503d805f81146106f2576040519150601f19603f3d011682016040523d82523d5f602084013e6106f7565b606091505b50915091508161070857601561071c565b8080602001905181019061071c9190610b41565b60030b949350505050565b80356001600160a01b038116811461073d575f80fd5b919050565b5f805f60608486031215610754575f80fd5b61075d84610727565b925061076b60208501610727565b915061077960408501610727565b90509250925092565b5f805f8060808587031215610795575f80fd5b61079e85610727565b93506107ac60208601610727565b92506107ba60408601610727565b9396929550929360600135925050565b634e487b7160e01b5f52604160045260245ffd5b604051601f8201601f1916810167ffffffffffffffff81118282101715610807576108076107ca565b604052919050565b5f8060408385031215610820575f80fd5b61082983610727565b915060208084013567ffffffffffffffff80821115610846575f80fd5b818601915086601f830112610859575f80fd5b81358181111561086b5761086b6107ca565b61087d601f8201601f191685016107de565b91508082528784828501011115610892575f80fd5b80848401858401375f848284010152508093505050509250929050565b5f5b838110156108c95781810151838201526020016108b1565b50505f910152565b5f81518084526108e88160208601602086016108af565b601f01601f19169290920160200192915050565b828152604060208201525f61091460408301846108d1565b949350505050565b5f67ffffffffffffffff821115610935576109356107ca565b5060051b60200190565b5f82601f83011261094e575f80fd5b8135602061096361095e8361091c565b6107de565b8083825260208201915060208460051b870101935086841115610984575f80fd5b602086015b848110156109a75761099a81610727565b8352918301918301610989565b509695505050505050565b8035600781900b811461073d575f80fd5b5f805f80608085870312156109d6575f80fd5b843567ffffffffffffffff808211156109ed575f80fd5b6109f98883890161093f565b9550602091508187013581811115610a0f575f80fd5b610a1b89828a0161093f565b955050604087013581811115610a2f575f80fd5b610a3b89828a0161093f565b945050606087013581811115610a4f575f80fd5b87019050601f81018813610a61575f80fd5b8035610a6f61095e8261091c565b81815260059190911b8201830190838101908a831115610a8d575f80fd5b928401925b82841015610ab257610aa3846109b2565b82529284019290840190610a92565b979a9699509497505050505050565b5f805f8060808587031215610ad4575f80fd5b610add85610727565b9350610aeb60208601610727565b9250610af960408601610727565b9150610b07606086016109b2565b905092959194509250565b634e487b7160e01b5f52603260045260245ffd5b5f8251610b378184602087016108af565b9190910192915050565b5f60208284031215610b51575f80fd5b81518060030b8114610b61575f80fd5b9392505050565b6001600160a01b0383168152604060208201525f61091460408301846108d1565b8215158152604060208201525f61091460408301846108d1565b602080825282518282018190525f919060409081850190868401855b82811015610c0a57815180516001600160a01b0390811686528782015181168887015286820151168686015260609081015160070b9085015260809093019290850190600101610bbf565b509197965050505050505056fea2646970667358221220f1a1f12b097858c4cc8e88d72e780efebeaca5729e9bf087780b4700756d2a9764736f6c63430008180033", "linkReferences": {}, "deployedLinkReferences": {} } diff --git a/contracts-abi/contracts/system-contracts/hedera-token-service/examples/hrc-904/CancelAirdrop.sol/CancelAirdrop.json b/contracts-abi/contracts/system-contracts/hedera-token-service/examples/hrc-904/CancelAirdrop.sol/CancelAirdrop.json index 93492afc3..350cbbb35 100644 --- a/contracts-abi/contracts/system-contracts/hedera-token-service/examples/hrc-904/CancelAirdrop.sol/CancelAirdrop.json +++ b/contracts-abi/contracts/system-contracts/hedera-token-service/examples/hrc-904/CancelAirdrop.sol/CancelAirdrop.json @@ -70,7 +70,7 @@ "type": "int64[]" } ], - "name": "cancelAirdrops", + "name": "cancelMultipleAirdrops", "outputs": [ { "internalType": "int64", diff --git a/contracts/system-contracts/hedera-token-service/examples/hrc-904/CancelAirdrop.sol b/contracts/system-contracts/hedera-token-service/examples/hrc-904/CancelAirdrop.sol index f7866d63c..473f9559a 100644 --- a/contracts/system-contracts/hedera-token-service/examples/hrc-904/CancelAirdrop.sol +++ b/contracts/system-contracts/hedera-token-service/examples/hrc-904/CancelAirdrop.sol @@ -71,7 +71,7 @@ contract CancelAirdrop is HederaTokenService { // @param tokens Array of token addresses for the pending airdrops // @param serials Array of serial numbers for NFT airdrops (use 0 for fungible tokens) // @return responseCode The response code from the batch cancel operation (22 = success) - function cancelAirdrops(address[] memory senders, address[] memory receivers, address[] memory tokens, int64[] memory serials) public returns (int64 responseCode) { + function cancelMultipleAirdrops(address[] memory senders, address[] memory receivers, address[] memory tokens, int64[] memory serials) public returns (int64 responseCode) { uint length = senders.length; IHederaTokenService.PendingAirdrop[] memory pendingAirdrops = new IHederaTokenService.PendingAirdrop[](length); for (uint i = 0; i < length; i++) { diff --git a/test/constants.js b/test/constants.js index 1e07319ec..0e95bd36d 100644 --- a/test/constants.js +++ b/test/constants.js @@ -204,11 +204,15 @@ const Contract = { CancunOpcodes: 'CancunOpcodes', KZGPointEvaluation: 'KZGPointEvaluation', StateRegistry: 'StateRegistry', + Airdrop: 'Airdrop', + CancelAirdrop: 'CancelAirdrop', }; const CALL_EXCEPTION = 'CALL_EXCEPTION'; const CONTRACT_REVERT_EXECUTED_CODE = 3; const GAS_LIMIT_1_000_000 = { gasLimit: 1_000_000 }; +const GAS_LIMIT_2_000_000 = { gasLimit: 2_000_000 }; +const GAS_LIMIT_5_000_000 = { gasLimit: 5_000_000 }; const GAS_LIMIT_10_000_000 = { gasLimit: 10_000_000 }; const GAS_LIMIT_800000 = { gasLimit: 800000 }; const GAS_LIMIT_8000000 = { gasLimit: 8000000 }; @@ -222,6 +226,8 @@ const HOUR = 60 * MINUTE; const DAY = 24 * HOUR; const WEEK = 7 * DAY; const GWEI = 1e9; +const HTS_SYSTEM_CONTRACT_ADDRESS = '0.0.359'; +const HAS_SYSTEM_CONTRACT_ADDRESS = '0.0.362'; module.exports = { Events, @@ -230,6 +236,8 @@ module.exports = { CALL_EXCEPTION, CONTRACT_REVERT_EXECUTED_CODE, GAS_LIMIT_1_000_000, + GAS_LIMIT_2_000_000, + GAS_LIMIT_5_000_000, GAS_LIMIT_10_000_000, GAS_LIMIT_800000, GAS_LIMIT_8000000, @@ -244,4 +252,6 @@ module.exports = { WEEK, WEI, GWEI, + HTS_SYSTEM_CONTRACT_ADDRESS, + HAS_SYSTEM_CONTRACT_ADDRESS, }; diff --git a/test/system-contracts/hedera-token-service/hrc-904/CancelAirdropContract.js b/test/system-contracts/hedera-token-service/hrc-904/CancelAirdropContract.js new file mode 100644 index 000000000..8233c3778 --- /dev/null +++ b/test/system-contracts/hedera-token-service/hrc-904/CancelAirdropContract.js @@ -0,0 +1,381 @@ +const { expect } = require('chai'); +const { ethers } = require('hardhat'); +const utils = require('../utils'); +const Constants = require('../../../constants'); + +describe('HRC-904 CancelAirdropContract Test Suite', function () { + let airdropContract; + let cancelAirdropContract; + let tokenCreateContract; + let erc20Contract; + let erc721Contract; + let signers; + let owner; + let accounts; + let receiver; + let walletIHRC904AccountFacade; + + async function setupToken() { + const tokenAddress = + await utils.createFungibleTokenWithSECP256K1AdminKeyWithoutKYC( + tokenCreateContract, + owner, + utils.getSignerCompressedPublicKey() + ); + + await utils.updateTokenKeysViaHapi( + tokenAddress, + [ + await airdropContract.getAddress(), + await tokenCreateContract.getAddress(), + ], + true, + true, + false, + true, + true, + true, + false + ); + + await utils.associateToken( + tokenCreateContract, + tokenAddress, + Constants.Contract.TokenCreateContract + ); + + return tokenAddress; + } + + async function setupNft() { + const nftTokenAddress = + await utils.createNonFungibleTokenWithSECP256K1AdminKeyWithoutKYC( + tokenCreateContract, + owner, + utils.getSignerCompressedPublicKey() + ); + + await utils.updateTokenKeysViaHapi( + nftTokenAddress, + [ + await airdropContract.getAddress(), + await tokenCreateContract.getAddress(), + ], + true, + true, + false, + true, + true, + true, + false + ); + + await utils.associateToken( + tokenCreateContract, + nftTokenAddress, + Constants.Contract.TokenCreateContract + ); + + return nftTokenAddress; + } + + async function createPendingAirdrops(count) { + const senders = []; + const receivers = []; + const tokens = []; + const serials = []; + const amounts = []; + + for (let i = 0; i < count; i++) { + const tokenAddress = await setupToken(); + const ftAmount = BigInt(i + 1); // Different amount for each airdrop + + const airdropTx = await airdropContract.tokenAirdrop( + tokenAddress, + owner, + receiver, + ftAmount, + { + value: BigInt('850000000000000000'), + gasLimit: 2_000_000, + } + ); + await airdropTx.wait(); + + senders.push(owner); + receivers.push(receiver); + tokens.push(tokenAddress); + serials.push(0); // 0 for fungible tokens + amounts.push(ftAmount); + } + + return { senders, receivers, tokens, serials, amounts }; + } + + before(async function () { + signers = await ethers.getSigners(); + airdropContract = await utils.deployContract(Constants.Contract.Airdrop); + cancelAirdropContract = await utils.deployContract( + Constants.Contract.CancelAirdrop + ); + + receiver = new ethers.Wallet( + ethers.hexlify(ethers.randomBytes(32)) + ).connect(ethers.provider); + + // Send some HBAR to activate the account + await signers[0].sendTransaction({ + to: receiver.address, + value: ethers.parseEther('100'), + }); + tokenCreateContract = await utils.deployContract( + Constants.Contract.TokenCreateContract + ); + erc20Contract = await utils.deployContract( + Constants.Contract.ERC20Contract + ); + erc721Contract = await utils.deployContract( + Constants.Contract.ERC721Contract + ); + owner = signers[0].address; + accounts = signers.slice(1, 3).map((s) => s.address); + + await utils.updateAccountKeysViaHapi([ + await airdropContract.getAddress(), + await tokenCreateContract.getAddress(), + await cancelAirdropContract.getAddress(), + ]); + + tokenAddress = await setupToken(); + + const IHRC904AccountFacade = new ethers.Interface( + (await hre.artifacts.readArtifact('IHRC904AccountFacade')).abi + ); + + walletIHRC904AccountFacade = new ethers.Contract( + receiver.address, + IHRC904AccountFacade, + receiver + ); + + // Disabling automatic associations for receiver so all airdrops will be pending + const disableAutoAssociations = + await walletIHRC904AccountFacade.setUnlimitedAutomaticAssociations( + false, + { + gasLimit: 2_000_000, + } + ); + await disableAutoAssociations.wait(); + }); + + it('should cancel a single pending fungible token airdrop', async function () { + const ftAmount = BigInt(1); + const sender = signers[0].address; + const tokenAddress = await setupToken(); + + const initialBalance = await erc20Contract.balanceOf( + tokenAddress, + receiver.address + ); + + const airdropTx = await airdropContract.tokenAirdrop( + tokenAddress, + sender, + receiver.address, + ftAmount, + { + value: BigInt('850000000000000000'), + gasLimit: 2_000_000, + } + ); + await airdropTx.wait(); + + const cancelTx = await cancelAirdropContract.cancelAirdrop( + sender, + receiver.address, + tokenAddress, + Constants.GAS_LIMIT_2_000_000 + ); + await cancelTx.wait(); + + const updatedBalance = await erc20Contract.balanceOf( + tokenAddress, + receiver.address + ); + expect(updatedBalance).to.equal(initialBalance); + }); + + it('should cancel a single pending NFT airdrop', async function () { + const sender = signers[0].address; + const nftTokenAddress = await setupNft(); + + const serialNumber = await utils.mintNFTToAddress( + tokenCreateContract, + nftTokenAddress + ); + + const airdropTx = await airdropContract.nftAirdrop( + nftTokenAddress, + sender, + receiver.address, + serialNumber, + { + value: BigInt('850000000000000000'), + gasLimit: 2_000_000, + } + ); + await airdropTx.wait(); + + const cancelTx = await cancelAirdropContract.cancelNFTAirdrop( + sender, + receiver.address, + nftTokenAddress, + serialNumber, + Constants.GAS_LIMIT_2_000_000 + ); + await cancelTx.wait(); + + const nftOwner = await erc721Contract.ownerOf( + nftTokenAddress, + serialNumber + ); + expect(nftOwner).to.equal(sender); + }); + + it('should cancel multiple pending fungible token airdrops', async function () { + const numAirdrops = 10; + const { senders, receivers, tokens, serials, amounts } = + await createPendingAirdrops(numAirdrops); + + const initialBalances = await Promise.all( + tokens.map(async (token) => erc20Contract.balanceOf(token, receiver)) + ); + + const cancelTx = await cancelAirdropContract.cancelMultipleAirdrops( + senders, + receivers, + tokens, + serials, + Constants.GAS_LIMIT_2_000_000 + ); + await cancelTx.wait(); + + for (let i = 0; i < tokens.length; i++) { + const updatedBalance = await erc20Contract.balanceOf(tokens[i], receiver); + expect(updatedBalance).to.equal(initialBalances[i]); + } + }); + + it('should fail when sender has no pending airdrops', async function () { + const sender = signers[1].address; + const tokenAddress = await setupToken(); + + const tx = await cancelAirdropContract.cancelAirdrop( + sender, + receiver, + tokenAddress, + Constants.GAS_LIMIT_2_000_000 + ); + const responseCode = await utils.getHTSResponseCode(tx.hash); + expect(responseCode).to.eq('367'); // NO_PENDING_REWARD code + }); + + it('should fail when sender account is invalid', async function () { + const invalidSender = ethers.Wallet.createRandom().address; + const tokenAddress = await setupToken(); + + const tx = await cancelAirdropContract.cancelAirdrop( + invalidSender, + receiver, + tokenAddress, + Constants.GAS_LIMIT_2_000_000 + ); + const responseCode = await utils.getHTSResponseCode(tx.hash); + expect(responseCode).to.eq('15'); // INVALID_ACCOUNT_ID code + }); + + it('should fail when receiver account is invalid', async function () { + const invalidReceiver = ethers.Wallet.createRandom().address; + const tokenAddress = await setupToken(); + + const tx = await cancelAirdropContract.cancelAirdrop( + owner, + invalidReceiver, + tokenAddress, + Constants.GAS_LIMIT_2_000_000 + ); + const responseCode = await utils.getHTSResponseCode(tx.hash); + expect(responseCode).to.eq('367'); // INVALID_PENDING_AIRDROP_ID code + }); + + it('should fail when token does not exist', async function () { + const invalidToken = ethers.Wallet.createRandom().address; + + try { + const tx = await cancelAirdropContract.cancelAirdrop( + owner, + receiver, + invalidToken, + Constants.GAS_LIMIT_2_000_000 + ); + await tx.wait(); + expect.fail('Should revert'); + } catch (error) { + expect(error.shortMessage).to.eq('transaction execution reverted'); + } + }); + + it('should fail when NFT does not exist', async function () { + const invalidNftToken = ethers.Wallet.createRandom().address; + const serialNumber = 1; + + try { + const tx = await cancelAirdropContract.cancelNFTAirdrop( + owner, + receiver, + invalidNftToken, + serialNumber, + Constants.GAS_LIMIT_2_000_000 + ); + await tx.wait(); + expect.fail('Should revert'); + } catch (error) { + expect(error.shortMessage).to.eq('transaction execution reverted'); + } + }); + + it('should fail when more than 10 pending airdrops provided', async function () { + try { + const { senders, receivers, tokens, serials } = + await createPendingAirdrops(11); + + const tx = await cancelAirdropContract.cancelMultipleAirdrops( + senders, + receivers, + tokens, + serials, + Constants.GAS_LIMIT_2_000_000 + ); + await tx.wait(); + expect.fail('Should revert'); + } catch (error) { + expect(error.shortMessage).to.eq('transaction execution reverted'); + } + }); + + it('should fail when NFT serial number does not exist', async function () { + const nftTokenAddress = await setupNft(); + const invalidSerialNumber = 999; + + const tx = await cancelAirdropContract.cancelNFTAirdrop( + owner, + receiver, + nftTokenAddress, + invalidSerialNumber, + Constants.GAS_LIMIT_2_000_000 + ); + const responseCode = await utils.getHTSResponseCode(tx.hash); + expect(responseCode).to.eq('367'); // INVALID_PENDING_AIRDROP_ID code + }); +}); diff --git a/test/system-contracts/hedera-token-service/utils.js b/test/system-contracts/hedera-token-service/utils.js index e20b11a6b..7b90fb9e8 100644 --- a/test/system-contracts/hedera-token-service/utils.js +++ b/test/system-contracts/hedera-token-service/utils.js @@ -36,6 +36,10 @@ const { ContractInfoQuery, } = require('@hashgraph/sdk'); const Constants = require('../../constants'); +const axios = require('axios'); +const { + getMirrorNodeUrl, +} = require('../native/evm-compatibility-ecrecover/utils'); class Utils { //createTokenCost is cost for creating the token, which is passed to the system-contracts. This is equivalent of 40 and 60hbars, any excess hbars are refunded. @@ -65,143 +69,16 @@ class Utils { DELEGETABLE_CONTRACT_ID: 4, }; - static async deployERC20Mock() { - const erc20MockFactory = await ethers.getContractFactory( - Constants.Path.HIP583_ERC20Mock - ); - const erc20Mock = await erc20MockFactory.deploy( - Constants.GAS_LIMIT_1_000_000 - ); - - return await ethers.getContractAt( - Constants.Path.HIP583_ERC20Mock, - await erc20Mock.getAddress() - ); - } - - static async deployERC721Mock() { - const erc721MockFactory = await ethers.getContractFactory( - Constants.Path.HIP583_ERC721Mock - ); - const erc721Mock = await erc721MockFactory.deploy( - Constants.GAS_LIMIT_1_000_000 - ); - - return await ethers.getContractAt( - Constants.Path.HIP583_ERC721Mock, - await erc721Mock.getAddress() - ); - } - - static async deployTokenCreateContract() { - const tokenCreateFactory = await ethers.getContractFactory( - Constants.Contract.TokenCreateContract - ); - const tokenCreate = await tokenCreateFactory.deploy( - Constants.GAS_LIMIT_1_000_000 - ); - - return await ethers.getContractAt( - Constants.Contract.TokenCreateContract, - await tokenCreate.getAddress() - ); - } - - static async deployTokenCreateCustomContract() { - const tokenCreateCustomFactory = await ethers.getContractFactory( - Constants.Contract.TokenCreateCustomContract - ); - const tokenCreateCustom = await tokenCreateCustomFactory.deploy( - Constants.GAS_LIMIT_1_000_000 - ); - - return await ethers.getContractAt( - Constants.Contract.TokenCreateCustomContract, - await tokenCreateCustom.getAddress() - ); - } - - static async deployTokenManagementContract() { - const tokenManagementFactory = await ethers.getContractFactory( - Constants.Contract.TokenManagementContract - ); - const tokenManagement = await tokenManagementFactory.deploy( - Constants.GAS_LIMIT_1_000_000 - ); - - return await ethers.getContractAt( - Constants.Contract.TokenManagementContract, - await tokenManagement.getAddress() - ); - } - - static async deployTokenQueryContract() { - const tokenQueryFactory = await ethers.getContractFactory( - Constants.Contract.TokenQueryContract - ); - const tokenQuery = await tokenQueryFactory.deploy( - Constants.GAS_LIMIT_1_000_000 - ); - - return await ethers.getContractAt( - Constants.Contract.TokenQueryContract, - await tokenQuery.getAddress() - ); - } - - static async deployTokenTransferContract() { - const tokenTransferFactory = await ethers.getContractFactory( - Constants.Contract.TokenTransferContract - ); - const tokenTransfer = await tokenTransferFactory.deploy( - Constants.GAS_LIMIT_1_000_000 - ); - - return await ethers.getContractAt( - Constants.Contract.TokenTransferContract, - await tokenTransfer.getAddress() - ); - } - - static async deployHRC719Contract() { - const hrcContractFactory = await ethers.getContractFactory( - Constants.Contract.HRC719Contract - ); - const hrcContract = await hrcContractFactory.deploy( - Constants.GAS_LIMIT_1_000_000 - ); - - return await ethers.getContractAt( - Constants.Contract.HRC719Contract, - await hrcContract.getAddress() - ); - } - - static async deployERC20Contract() { - const erc20ContractFactory = await ethers.getContractFactory( - Constants.Contract.ERC20Contract - ); - const erc20Contract = await erc20ContractFactory.deploy( - Constants.GAS_LIMIT_1_000_000 - ); - - return await ethers.getContractAt( - Constants.Contract.ERC20Contract, - await erc20Contract.getAddress() - ); - } - - static async deployERC721Contract() { - const erc721ContractFactory = await ethers.getContractFactory( - Constants.Contract.ERC721Contract - ); - const erc721Contract = await erc721ContractFactory.deploy( - Constants.GAS_LIMIT_1_000_000 - ); + static async deployContract( + contractPath, + gasLimit = Constants.GAS_LIMIT_1_000_000 + ) { + const factory = await ethers.getContractFactory(contractPath); + const contract = await factory.deploy(gasLimit); return await ethers.getContractAt( - Constants.Contract.ERC721Contract, - await erc721Contract.getAddress() + contractPath, + await contract.getAddress() ); } @@ -942,6 +819,30 @@ class Utils { return; } } + + static async getHTSResponseCode(txHash) { + const network = hre.network.name; + const mirrorNodeUrl = getMirrorNodeUrl(network); + const res = await axios.get( + `${mirrorNodeUrl}/contracts/results/${txHash}/actions` + ); + const precompileAction = res.data.actions.find( + (x) => x.recipient === Constants.HTS_SYSTEM_CONTRACT_ADDRESS + ); + return BigInt(precompileAction.result_data).toString(); + } + + static async getHASResponseCode(txHash) { + const network = hre.network.name; + const mirrorNodeUrl = getMirrorNodeUrl(network); + const res = await axios.get( + `${mirrorNodeUrl}/contracts/results/${txHash}/actions` + ); + const precompileAction = res.data.actions.find( + (x) => x.recipient === Constants.HAS_SYSTEM_CONTRACT_ADDRESS + ); + return BigInt(precompileAction.result_data).toString(); + } } module.exports = Utils; From 3edc3f61e63d5ea1a9bd031404553e805ff18eff Mon Sep 17 00:00:00 2001 From: Simeon Nakov Date: Mon, 20 Jan 2025 14:14:15 +0200 Subject: [PATCH 2/9] feat: changes test suite string Signed-off-by: Simeon Nakov --- .../hedera-token-service/hrc-904/CancelAirdropContract.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/system-contracts/hedera-token-service/hrc-904/CancelAirdropContract.js b/test/system-contracts/hedera-token-service/hrc-904/CancelAirdropContract.js index 8233c3778..33595f1c8 100644 --- a/test/system-contracts/hedera-token-service/hrc-904/CancelAirdropContract.js +++ b/test/system-contracts/hedera-token-service/hrc-904/CancelAirdropContract.js @@ -3,7 +3,7 @@ const { ethers } = require('hardhat'); const utils = require('../utils'); const Constants = require('../../../constants'); -describe('HRC-904 CancelAirdropContract Test Suite', function () { +describe('HIP904 CancelAirdropContract Test Suite', function () { let airdropContract; let cancelAirdropContract; let tokenCreateContract; From ae54abb4807d19b1554dc1a4e23ed6963a1112df Mon Sep 17 00:00:00 2001 From: Simeon Nakov Date: Mon, 20 Jan 2025 14:48:42 +0200 Subject: [PATCH 3/9] feat: changed code comment Signed-off-by: Simeon Nakov --- .../hedera-token-service/hrc-904/CancelAirdropContract.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/system-contracts/hedera-token-service/hrc-904/CancelAirdropContract.js b/test/system-contracts/hedera-token-service/hrc-904/CancelAirdropContract.js index 33595f1c8..8d4974d02 100644 --- a/test/system-contracts/hedera-token-service/hrc-904/CancelAirdropContract.js +++ b/test/system-contracts/hedera-token-service/hrc-904/CancelAirdropContract.js @@ -278,7 +278,7 @@ describe('HIP904 CancelAirdropContract Test Suite', function () { Constants.GAS_LIMIT_2_000_000 ); const responseCode = await utils.getHTSResponseCode(tx.hash); - expect(responseCode).to.eq('367'); // NO_PENDING_REWARD code + expect(responseCode).to.eq('367'); // INVALID_PENDING_AIRDROP_ID code }); it('should fail when sender account is invalid', async function () { From 282d154ab7b3dc677cfdc08cf7529e9354826241 Mon Sep 17 00:00:00 2001 From: Simeon Nakov Date: Fri, 24 Jan 2025 15:15:40 +0200 Subject: [PATCH 4/9] skipped tests temporarily Signed-off-by: Simeon Nakov --- .../hedera-token-service/hrc-904/CancelAirdropContract.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/test/system-contracts/hedera-token-service/hrc-904/CancelAirdropContract.js b/test/system-contracts/hedera-token-service/hrc-904/CancelAirdropContract.js index 8d4974d02..5037738b0 100644 --- a/test/system-contracts/hedera-token-service/hrc-904/CancelAirdropContract.js +++ b/test/system-contracts/hedera-token-service/hrc-904/CancelAirdropContract.js @@ -309,7 +309,9 @@ describe('HIP904 CancelAirdropContract Test Suite', function () { expect(responseCode).to.eq('367'); // INVALID_PENDING_AIRDROP_ID code }); - it('should fail when token does not exist', async function () { + // TODO: The following 3 tests are skipped because they are not supported by the current implementation in services + // They do not return the correct error code, therefore they will be skipped until the implementation is updated + it.skip('should fail when token does not exist', async function () { const invalidToken = ethers.Wallet.createRandom().address; try { @@ -326,7 +328,7 @@ describe('HIP904 CancelAirdropContract Test Suite', function () { } }); - it('should fail when NFT does not exist', async function () { + it.skip('should fail when NFT does not exist', async function () { const invalidNftToken = ethers.Wallet.createRandom().address; const serialNumber = 1; @@ -345,7 +347,7 @@ describe('HIP904 CancelAirdropContract Test Suite', function () { } }); - it('should fail when more than 10 pending airdrops provided', async function () { + it.skip('should fail when more than 10 pending airdrops provided', async function () { try { const { senders, receivers, tokens, serials } = await createPendingAirdrops(11); From c4601424bdda0ee5dc66231f12d6f41666ebc258 Mon Sep 17 00:00:00 2001 From: Simeon Nakov Date: Fri, 17 Jan 2025 16:44:45 +0200 Subject: [PATCH 5/9] feat: added test coverage of CancelAirdrop.sol Signed-off-by: Simeon Nakov --- test/constants.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/constants.js b/test/constants.js index c310c62ab..257b1da3a 100644 --- a/test/constants.js +++ b/test/constants.js @@ -207,6 +207,8 @@ const Contract = { Airdrop: 'Airdrop', CancelAirdrop: 'CancelAirdrop', AliasAccountUtility: 'AliasAccountUtility', + Airdrop: 'Airdrop', + CancelAirdrop: 'CancelAirdrop', }; const CALL_EXCEPTION = 'CALL_EXCEPTION'; From f00d8fc0d5bb8211a6b8e4a8fce7e52db6f438ca Mon Sep 17 00:00:00 2001 From: Simeon Nakov Date: Mon, 27 Jan 2025 11:42:18 +0200 Subject: [PATCH 6/9] revert removal of deploy functions Signed-off-by: Simeon Nakov --- .../hedera-token-service/utils.js | 140 ++++++++++++++++++ 1 file changed, 140 insertions(+) diff --git a/test/system-contracts/hedera-token-service/utils.js b/test/system-contracts/hedera-token-service/utils.js index 7b90fb9e8..f308d06d2 100644 --- a/test/system-contracts/hedera-token-service/utils.js +++ b/test/system-contracts/hedera-token-service/utils.js @@ -82,6 +82,146 @@ class Utils { ); } + static async deployERC20Mock() { + const erc20MockFactory = await ethers.getContractFactory( + Constants.Path.HIP583_ERC20Mock + ); + const erc20Mock = await erc20MockFactory.deploy( + Constants.GAS_LIMIT_1_000_000 + ); + + return await ethers.getContractAt( + Constants.Path.HIP583_ERC20Mock, + await erc20Mock.getAddress() + ); + } + + static async deployERC721Mock() { + const erc721MockFactory = await ethers.getContractFactory( + Constants.Path.HIP583_ERC721Mock + ); + const erc721Mock = await erc721MockFactory.deploy( + Constants.GAS_LIMIT_1_000_000 + ); + + return await ethers.getContractAt( + Constants.Path.HIP583_ERC721Mock, + await erc721Mock.getAddress() + ); + } + + static async deployTokenCreateContract() { + const tokenCreateFactory = await ethers.getContractFactory( + Constants.Contract.TokenCreateContract + ); + const tokenCreate = await tokenCreateFactory.deploy( + Constants.GAS_LIMIT_1_000_000 + ); + + return await ethers.getContractAt( + Constants.Contract.TokenCreateContract, + await tokenCreate.getAddress() + ); + } + + static async deployTokenCreateCustomContract() { + const tokenCreateCustomFactory = await ethers.getContractFactory( + Constants.Contract.TokenCreateCustomContract + ); + const tokenCreateCustom = await tokenCreateCustomFactory.deploy( + Constants.GAS_LIMIT_1_000_000 + ); + + return await ethers.getContractAt( + Constants.Contract.TokenCreateCustomContract, + await tokenCreateCustom.getAddress() + ); + } + + static async deployTokenManagementContract() { + const tokenManagementFactory = await ethers.getContractFactory( + Constants.Contract.TokenManagementContract + ); + const tokenManagement = await tokenManagementFactory.deploy( + Constants.GAS_LIMIT_1_000_000 + ); + + return await ethers.getContractAt( + Constants.Contract.TokenManagementContract, + await tokenManagement.getAddress() + ); + } + + static async deployTokenQueryContract() { + const tokenQueryFactory = await ethers.getContractFactory( + Constants.Contract.TokenQueryContract + ); + const tokenQuery = await tokenQueryFactory.deploy( + Constants.GAS_LIMIT_1_000_000 + ); + + return await ethers.getContractAt( + Constants.Contract.TokenQueryContract, + await tokenQuery.getAddress() + ); + } + + static async deployTokenTransferContract() { + const tokenTransferFactory = await ethers.getContractFactory( + Constants.Contract.TokenTransferContract + ); + const tokenTransfer = await tokenTransferFactory.deploy( + Constants.GAS_LIMIT_1_000_000 + ); + + return await ethers.getContractAt( + Constants.Contract.TokenTransferContract, + await tokenTransfer.getAddress() + ); + } + + static async deployHRC719Contract() { + const hrcContractFactory = await ethers.getContractFactory( + Constants.Contract.HRC719Contract + ); + const hrcContract = await hrcContractFactory.deploy( + Constants.GAS_LIMIT_1_000_000 + ); + + return await ethers.getContractAt( + Constants.Contract.HRC719Contract, + await hrcContract.getAddress() + ); + } + + static async deployERC20Contract() { + const erc20ContractFactory = await ethers.getContractFactory( + Constants.Contract.ERC20Contract + ); + const erc20Contract = await erc20ContractFactory.deploy( + Constants.GAS_LIMIT_1_000_000 + ); + + return await ethers.getContractAt( + Constants.Contract.ERC20Contract, + await erc20Contract.getAddress() + ); + } + + static async deployERC721Contract() { + const erc721ContractFactory = await ethers.getContractFactory( + Constants.Contract.ERC721Contract + ); + const erc721Contract = await erc721ContractFactory.deploy( + Constants.GAS_LIMIT_1_000_000 + ); + + return await ethers.getContractAt( + Constants.Contract.ERC721Contract, + await erc721Contract.getAddress() + ); + } + static async createFungibleToken(contract, treasury) { const tokenAddressTx = await contract.createFungibleTokenPublic(treasury, { value: BigInt(this.createTokenCost), From 34f663a520984739556eb99ec8e5192c8a2b26a2 Mon Sep 17 00:00:00 2001 From: Simeon Nakov Date: Wed, 29 Jan 2025 10:41:22 +0200 Subject: [PATCH 7/9] exported magic string as constant Signed-off-by: Simeon Nakov --- test/constants.js | 2 ++ .../hedera-token-service/hrc-904/CancelAirdropContract.js | 6 +++--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/test/constants.js b/test/constants.js index 257b1da3a..f2814de44 100644 --- a/test/constants.js +++ b/test/constants.js @@ -219,6 +219,7 @@ const GAS_LIMIT_5_000_000 = { gasLimit: 5_000_000 }; const GAS_LIMIT_10_000_000 = { gasLimit: 10_000_000 }; const GAS_LIMIT_800000 = { gasLimit: 800000 }; const GAS_LIMIT_8000000 = { gasLimit: 8000000 }; +const ONE_HBAR = BigInt('850000000000000000'); const TOKEN_NAME = 'tokenName'; const TOKEN_SYMBOL = 'tokenSymbol'; const TOKEN_URL = 'tokenUrl'; @@ -257,4 +258,5 @@ module.exports = { GWEI, HTS_SYSTEM_CONTRACT_ADDRESS, HAS_SYSTEM_CONTRACT_ADDRESS, + ONE_HBAR, }; diff --git a/test/system-contracts/hedera-token-service/hrc-904/CancelAirdropContract.js b/test/system-contracts/hedera-token-service/hrc-904/CancelAirdropContract.js index 5037738b0..1f242f202 100644 --- a/test/system-contracts/hedera-token-service/hrc-904/CancelAirdropContract.js +++ b/test/system-contracts/hedera-token-service/hrc-904/CancelAirdropContract.js @@ -96,7 +96,7 @@ describe('HIP904 CancelAirdropContract Test Suite', function () { receiver, ftAmount, { - value: BigInt('850000000000000000'), + value: Constants.ONE_HBAR, gasLimit: 2_000_000, } ); @@ -185,7 +185,7 @@ describe('HIP904 CancelAirdropContract Test Suite', function () { receiver.address, ftAmount, { - value: BigInt('850000000000000000'), + value: Constants.ONE_HBAR, gasLimit: 2_000_000, } ); @@ -221,7 +221,7 @@ describe('HIP904 CancelAirdropContract Test Suite', function () { receiver.address, serialNumber, { - value: BigInt('850000000000000000'), + value: Constants.ONE_HBAR, gasLimit: 2_000_000, } ); From 6e3785d281ce24c2d036e27d68a4b9bc5d398ce4 Mon Sep 17 00:00:00 2001 From: Simeon Nakov Date: Wed, 29 Jan 2025 10:48:22 +0200 Subject: [PATCH 8/9] more clear comments for skipped tests Signed-off-by: Simeon Nakov --- .../hedera-token-service/hrc-904/CancelAirdropContract.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/test/system-contracts/hedera-token-service/hrc-904/CancelAirdropContract.js b/test/system-contracts/hedera-token-service/hrc-904/CancelAirdropContract.js index 1f242f202..303d3faca 100644 --- a/test/system-contracts/hedera-token-service/hrc-904/CancelAirdropContract.js +++ b/test/system-contracts/hedera-token-service/hrc-904/CancelAirdropContract.js @@ -309,8 +309,8 @@ describe('HIP904 CancelAirdropContract Test Suite', function () { expect(responseCode).to.eq('367'); // INVALID_PENDING_AIRDROP_ID code }); - // TODO: The following 3 tests are skipped because they are not supported by the current implementation in services - // They do not return the correct error code, therefore they will be skipped until the implementation is updated + // TODO: Test is skipped because current implementation does not return correct error code for non-existent tokens + // https://github.com/hashgraph/hedera-services/issues/17534 it.skip('should fail when token does not exist', async function () { const invalidToken = ethers.Wallet.createRandom().address; @@ -328,6 +328,8 @@ describe('HIP904 CancelAirdropContract Test Suite', function () { } }); + // TODO: Test is skipped because current implementation does not return correct error code for non-existent NFTs + // https://github.com/hashgraph/hedera-services/issues/17534 it.skip('should fail when NFT does not exist', async function () { const invalidNftToken = ethers.Wallet.createRandom().address; const serialNumber = 1; @@ -347,6 +349,8 @@ describe('HIP904 CancelAirdropContract Test Suite', function () { } }); + // TODO: Test is skipped because current implementation does not support checking for maximum number of pending airdrops + // https://github.com/hashgraph/hedera-services/issues/17534 it.skip('should fail when more than 10 pending airdrops provided', async function () { try { const { senders, receivers, tokens, serials } = From 4dc5c46404f5ca28bbc8190bea87fe0f70d316fc Mon Sep 17 00:00:00 2001 From: Simeon Nakov Date: Thu, 30 Jan 2025 16:36:35 +0200 Subject: [PATCH 9/9] requested changes from review Signed-off-by: Simeon Nakov --- test/constants.js | 2 +- .../hrc-904/CancelAirdropContract.js | 186 +++++++----------- .../hedera-token-service/utils.js | 112 +++++++++++ 3 files changed, 186 insertions(+), 114 deletions(-) diff --git a/test/constants.js b/test/constants.js index f2814de44..0f4bc4dd5 100644 --- a/test/constants.js +++ b/test/constants.js @@ -219,7 +219,7 @@ const GAS_LIMIT_5_000_000 = { gasLimit: 5_000_000 }; const GAS_LIMIT_10_000_000 = { gasLimit: 10_000_000 }; const GAS_LIMIT_800000 = { gasLimit: 800000 }; const GAS_LIMIT_8000000 = { gasLimit: 8000000 }; -const ONE_HBAR = BigInt('850000000000000000'); +const ONE_HBAR = ethers.parseEther('1'); const TOKEN_NAME = 'tokenName'; const TOKEN_SYMBOL = 'tokenSymbol'; const TOKEN_URL = 'tokenUrl'; diff --git a/test/system-contracts/hedera-token-service/hrc-904/CancelAirdropContract.js b/test/system-contracts/hedera-token-service/hrc-904/CancelAirdropContract.js index 303d3faca..4d524d11f 100644 --- a/test/system-contracts/hedera-token-service/hrc-904/CancelAirdropContract.js +++ b/test/system-contracts/hedera-token-service/hrc-904/CancelAirdropContract.js @@ -1,3 +1,23 @@ +/*- + * + * Hedera Smart Contracts + * + * Copyright (C) 2025 Hedera Hashgraph, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + const { expect } = require('chai'); const { ethers } = require('hardhat'); const utils = require('../utils'); @@ -11,106 +31,7 @@ describe('HIP904 CancelAirdropContract Test Suite', function () { let erc721Contract; let signers; let owner; - let accounts; let receiver; - let walletIHRC904AccountFacade; - - async function setupToken() { - const tokenAddress = - await utils.createFungibleTokenWithSECP256K1AdminKeyWithoutKYC( - tokenCreateContract, - owner, - utils.getSignerCompressedPublicKey() - ); - - await utils.updateTokenKeysViaHapi( - tokenAddress, - [ - await airdropContract.getAddress(), - await tokenCreateContract.getAddress(), - ], - true, - true, - false, - true, - true, - true, - false - ); - - await utils.associateToken( - tokenCreateContract, - tokenAddress, - Constants.Contract.TokenCreateContract - ); - - return tokenAddress; - } - - async function setupNft() { - const nftTokenAddress = - await utils.createNonFungibleTokenWithSECP256K1AdminKeyWithoutKYC( - tokenCreateContract, - owner, - utils.getSignerCompressedPublicKey() - ); - - await utils.updateTokenKeysViaHapi( - nftTokenAddress, - [ - await airdropContract.getAddress(), - await tokenCreateContract.getAddress(), - ], - true, - true, - false, - true, - true, - true, - false - ); - - await utils.associateToken( - tokenCreateContract, - nftTokenAddress, - Constants.Contract.TokenCreateContract - ); - - return nftTokenAddress; - } - - async function createPendingAirdrops(count) { - const senders = []; - const receivers = []; - const tokens = []; - const serials = []; - const amounts = []; - - for (let i = 0; i < count; i++) { - const tokenAddress = await setupToken(); - const ftAmount = BigInt(i + 1); // Different amount for each airdrop - - const airdropTx = await airdropContract.tokenAirdrop( - tokenAddress, - owner, - receiver, - ftAmount, - { - value: Constants.ONE_HBAR, - gasLimit: 2_000_000, - } - ); - await airdropTx.wait(); - - senders.push(owner); - receivers.push(receiver); - tokens.push(tokenAddress); - serials.push(0); // 0 for fungible tokens - amounts.push(ftAmount); - } - - return { senders, receivers, tokens, serials, amounts }; - } before(async function () { signers = await ethers.getSigners(); @@ -119,9 +40,7 @@ describe('HIP904 CancelAirdropContract Test Suite', function () { Constants.Contract.CancelAirdrop ); - receiver = new ethers.Wallet( - ethers.hexlify(ethers.randomBytes(32)) - ).connect(ethers.provider); + receiver = ethers.Wallet.createRandom().connect(ethers.provider); // Send some HBAR to activate the account await signers[0].sendTransaction({ @@ -138,7 +57,6 @@ describe('HIP904 CancelAirdropContract Test Suite', function () { Constants.Contract.ERC721Contract ); owner = signers[0].address; - accounts = signers.slice(1, 3).map((s) => s.address); await utils.updateAccountKeysViaHapi([ await airdropContract.getAddress(), @@ -146,13 +64,17 @@ describe('HIP904 CancelAirdropContract Test Suite', function () { await cancelAirdropContract.getAddress(), ]); - tokenAddress = await setupToken(); + tokenAddress = await utils.setupToken( + tokenCreateContract, + owner, + airdropContract + ); const IHRC904AccountFacade = new ethers.Interface( (await hre.artifacts.readArtifact('IHRC904AccountFacade')).abi ); - walletIHRC904AccountFacade = new ethers.Contract( + const walletIHRC904AccountFacade = new ethers.Contract( receiver.address, IHRC904AccountFacade, receiver @@ -172,7 +94,11 @@ describe('HIP904 CancelAirdropContract Test Suite', function () { it('should cancel a single pending fungible token airdrop', async function () { const ftAmount = BigInt(1); const sender = signers[0].address; - const tokenAddress = await setupToken(); + const tokenAddress = await utils.setupToken( + tokenCreateContract, + owner, + airdropContract + ); const initialBalance = await erc20Contract.balanceOf( tokenAddress, @@ -208,7 +134,12 @@ describe('HIP904 CancelAirdropContract Test Suite', function () { it('should cancel a single pending NFT airdrop', async function () { const sender = signers[0].address; - const nftTokenAddress = await setupNft(); + const nftTokenAddress = await utils.setupNft( + tokenCreateContract, + owner, + airdropContract, + cancelAirdropContract + ); const serialNumber = await utils.mintNFTToAddress( tokenCreateContract, @@ -246,7 +177,13 @@ describe('HIP904 CancelAirdropContract Test Suite', function () { it('should cancel multiple pending fungible token airdrops', async function () { const numAirdrops = 10; const { senders, receivers, tokens, serials, amounts } = - await createPendingAirdrops(numAirdrops); + await utils.createPendingAirdrops( + numAirdrops, + tokenCreateContract, + owner, + airdropContract, + receiver + ); const initialBalances = await Promise.all( tokens.map(async (token) => erc20Contract.balanceOf(token, receiver)) @@ -269,7 +206,11 @@ describe('HIP904 CancelAirdropContract Test Suite', function () { it('should fail when sender has no pending airdrops', async function () { const sender = signers[1].address; - const tokenAddress = await setupToken(); + const tokenAddress = await utils.setupToken( + tokenCreateContract, + owner, + airdropContract + ); const tx = await cancelAirdropContract.cancelAirdrop( sender, @@ -283,7 +224,11 @@ describe('HIP904 CancelAirdropContract Test Suite', function () { it('should fail when sender account is invalid', async function () { const invalidSender = ethers.Wallet.createRandom().address; - const tokenAddress = await setupToken(); + const tokenAddress = await utils.setupToken( + tokenCreateContract, + owner, + airdropContract + ); const tx = await cancelAirdropContract.cancelAirdrop( invalidSender, @@ -297,7 +242,11 @@ describe('HIP904 CancelAirdropContract Test Suite', function () { it('should fail when receiver account is invalid', async function () { const invalidReceiver = ethers.Wallet.createRandom().address; - const tokenAddress = await setupToken(); + const tokenAddress = await utils.setupToken( + tokenCreateContract, + owner, + airdropContract + ); const tx = await cancelAirdropContract.cancelAirdrop( owner, @@ -354,7 +303,13 @@ describe('HIP904 CancelAirdropContract Test Suite', function () { it.skip('should fail when more than 10 pending airdrops provided', async function () { try { const { senders, receivers, tokens, serials } = - await createPendingAirdrops(11); + await utils.createPendingAirdrops( + 11, + tokenCreateContract, + owner, + airdropContract, + receiver + ); const tx = await cancelAirdropContract.cancelMultipleAirdrops( senders, @@ -371,7 +326,12 @@ describe('HIP904 CancelAirdropContract Test Suite', function () { }); it('should fail when NFT serial number does not exist', async function () { - const nftTokenAddress = await setupNft(); + const nftTokenAddress = await utils.setupNft( + tokenCreateContract, + owner, + airdropContract, + cancelAirdropContract + ); const invalidSerialNumber = 999; const tx = await cancelAirdropContract.cancelNFTAirdrop( diff --git a/test/system-contracts/hedera-token-service/utils.js b/test/system-contracts/hedera-token-service/utils.js index f308d06d2..092784c6c 100644 --- a/test/system-contracts/hedera-token-service/utils.js +++ b/test/system-contracts/hedera-token-service/utils.js @@ -983,6 +983,118 @@ class Utils { ); return BigInt(precompileAction.result_data).toString(); } + + static async setupNft( + tokenCreateContract, + owner, + airdropContract, + cancelAirdropContract + ) { + const nftTokenAddress = + await this.createNonFungibleTokenWithSECP256K1AdminKeyWithoutKYC( + tokenCreateContract, + owner, + this.getSignerCompressedPublicKey() + ); + + await this.updateTokenKeysViaHapi( + nftTokenAddress, + [ + await airdropContract.getAddress(), + await tokenCreateContract.getAddress(), + ], + true, + true, + false, + true, + true, + true, + false + ); + + await this.associateToken( + tokenCreateContract, + nftTokenAddress, + Constants.Contract.TokenCreateContract + ); + + return nftTokenAddress; + } + + static async setupToken(tokenCreateContract, owner, airdropContract) { + const tokenAddress = + await this.createFungibleTokenWithSECP256K1AdminKeyWithoutKYC( + tokenCreateContract, + owner, + this.getSignerCompressedPublicKey() + ); + + await this.updateTokenKeysViaHapi( + tokenAddress, + [ + await airdropContract.getAddress(), + await tokenCreateContract.getAddress(), + ], + true, + true, + false, + true, + true, + true, + false + ); + + await this.associateToken( + tokenCreateContract, + tokenAddress, + Constants.Contract.TokenCreateContract + ); + + return tokenAddress; + } + + static async createPendingAirdrops( + count, + tokenCreateContract, + owner, + airdropContract, + receiver + ) { + const senders = []; + const receivers = []; + const tokens = []; + const serials = []; + const amounts = []; + + for (let i = 0; i < count; i++) { + const tokenAddress = await this.setupToken( + tokenCreateContract, + owner, + airdropContract + ); + const ftAmount = BigInt(i + 1); // Different amount for each airdrop + + const airdropTx = await airdropContract.tokenAirdrop( + tokenAddress, + owner, + receiver, + ftAmount, + { + value: Constants.ONE_HBAR, + gasLimit: 2_000_000, + } + ); + await airdropTx.wait(); + + senders.push(owner); + receivers.push(receiver); + tokens.push(tokenAddress); + serials.push(0); // 0 for fungible tokens + amounts.push(ftAmount); + } + + return { senders, receivers, tokens, serials, amounts }; + } } module.exports = Utils;