diff --git a/contracts/solidity/address/Address.sol b/contracts/solidity/address/Address.sol new file mode 100644 index 000000000..b9dc3de3e --- /dev/null +++ b/contracts/solidity/address/Address.sol @@ -0,0 +1,67 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.20; + +contract AddressContract { + receive() external payable {} + string message = "Hello World from AddressContract!"; + event emitMessage(string a); + event res(bool answer, bytes data); + + function getAddressBalance(address addressToQuery) external view returns (uint256) { + return addressToQuery.balance; + } + + function getAddressCode(address addressToQuery) external view returns (bytes memory) { + return addressToQuery.code; + } + + function getAddressCodeHash(address addressToQuery) external view returns (bytes32) { + return addressToQuery.codehash; + } + + function transferTo(address payable addressToQuery, uint amount) external { + return addressToQuery.transfer(amount); + } + + function sendTo(address payable addressToQuery, uint amount) external { + bool answer = addressToQuery.send(amount); + require(answer, "Error sending"); + } + + function callAddr(address payable addressToQuery, uint amount) external { + (bool answer,) = addressToQuery.call{value: amount}(""); + require(answer, "Error calling"); + } + + function callAddrWithSig(address payable addressToQuery, uint amount, string memory functionSig) external payable returns (bytes memory){ + (bool answer, bytes memory data) = addressToQuery.call{gas: 900000, value: amount}(abi.encodeWithSignature(functionSig)); + require(answer, "Error calling"); + emit res(answer, data); + + return data; + } + + function delegate(address payable addressToQuery, string memory functionSig) external payable returns (bytes memory){ + (bool success, bytes memory data) = addressToQuery.delegatecall{gas: 90000000}(abi.encodeWithSignature(functionSig)); + require(success, "Error calling"); + emit res(success, data); + + return data; + } + + function staticCall(address payable addressToQuery, string memory functionSig) external payable returns (bytes memory){ + (bool answer, bytes memory data) = addressToQuery.staticcall(abi.encodeWithSignature(functionSig)); + require(answer, "Error calling"); + emit res(answer, data); + + return data; + } + + function staticCallSet(address payable addressToQuery, string memory functionSig, uint number) external payable returns (bytes memory){ + (bool answer, bytes memory data) = addressToQuery.staticcall(abi.encodeWithSignature(functionSig, number)); + require(answer, "Error calling"); + emit res(answer, data); + + return data; + } +} diff --git a/contracts/solidity/address/Recipient.sol b/contracts/solidity/address/Recipient.sol new file mode 100644 index 000000000..3d1c181fc --- /dev/null +++ b/contracts/solidity/address/Recipient.sol @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.20; + +contract Recipient { + receive() external payable {} + event msgValue(uint256 value); + event emitMessage(string message); + string message = "Hello World from Recipient contract!"; + uint myNumber = 5; + + function getNumber() external view returns (uint) { + return myNumber; + } + + function setNumber(uint number) external returns (uint) { + return myNumber = number; + } + + function getMessageValue() external payable { + emit msgValue(msg.value); + } + + function helloWorldMessage() external { + emit emitMessage(message); + } + + fallback() external payable { + + } +} diff --git a/test/constants.js b/test/constants.js index 21b0c35cc..df8cb60ae 100644 --- a/test/constants.js +++ b/test/constants.js @@ -105,6 +105,8 @@ const Contract = { Transaction: 'Transaction', MessageFrameAddresses: 'MessageFrameAddresses', New: 'New', + AddressContract: 'AddressContract', + Recipient: 'Recipient', } const CALL_EXCEPTION = 'CALL_EXCEPTION' diff --git a/test/solidity/address/address.js b/test/solidity/address/address.js new file mode 100644 index 000000000..9b9f049e9 --- /dev/null +++ b/test/solidity/address/address.js @@ -0,0 +1,146 @@ +const { expect } = require('chai') +const { ethers } = require('hardhat') +const Constants = require('../../constants') +const Utils = require('../../hts-precompile/utils') + +const TOP_UP_AMOUNT = ethers.utils.parseEther('1.0'); +const TRANSFER_AMOUNT = 1 + +describe('Solidity Address tests:', function () { + let signers,contract, wallet, walletAddr, recipientContract, recipientAddr; + + const tinybarToWeibar = (amount) => amount.mul(Utils.tinybarToWeibarCoef) + const weibarTotinybar = (amount) => amount.div(Utils.tinybarToWeibarCoef) + + before(async function () { + signers = await ethers.getSigners() + wallet = signers[0]; + walletAddr = await wallet.getAddress() + + //deploy test contract + const factory = await ethers.getContractFactory(Constants.Contract.AddressContract) + contract = await factory.deploy() + await contract.deployed(); + + //deploy test contract + const calledFactory = await ethers.getContractFactory(Constants.Contract.Recipient) + recipientContract = await calledFactory.deploy() + await recipientContract.deployed(); + recipientAddr = await recipientContract.address; + + //top up the test contract with some funds + let tx = { + to: contract.address, + value: TOP_UP_AMOUNT + } + const topUpRes = await wallet.sendTransaction(tx) + topUpRes.wait(); + }) + + it('should verify solidity functionality:
.balance', async function () { + const balance = await wallet.getBalance(); + const res = await contract.getAddressBalance(walletAddr); + + expect(tinybarToWeibar(res)).to.equal(balance); + expect(tinybarToWeibar(res).gt(0)).to.be.true; + }) + + it('should verify solidity functionality: .code', async function () { + const walletAddrCodeRes = await contract.getAddressCode(walletAddr); + const contractAddrCodeRes = await contract.getAddressCode(contract.address); + + expect(walletAddrCodeRes).to.exist; + expect(walletAddrCodeRes).to.equal('0x'); + expect(contractAddrCodeRes).to.exist; + expect(contractAddrCodeRes).to.not.equal('0x'); + expect(contractAddrCodeRes.length > 2).to.be.true; + }) + + it('should verify solidity functionality: .codehash', async function () { + const walletAddrCodeRes = await contract.getAddressCode(walletAddr); + const contractAddrCodeRes = await contract.getAddressCode(contract.address); + const hashedWalletCode = ethers.utils.keccak256(walletAddrCodeRes); + const hashedContractCode = ethers.utils.keccak256(contractAddrCodeRes); + const walletAddrResHash = await contract.getAddressCodeHash(walletAddr); + const contractAddrResHash = await contract.getAddressCodeHash(contract.address); + + expect(hashedWalletCode).to.equal(walletAddrResHash); + expect(hashedContractCode).to.equal(contractAddrResHash); + }) + + it('should verify solidity functionality: .transfer', async function () { + const recipientBalanceInitial = await ethers.provider.getBalance(recipientAddr); + + const tx = await contract.transferTo(recipientAddr, TRANSFER_AMOUNT); + await tx.wait(); + + const recipientBalanceFinal = await ethers.provider.getBalance(recipientAddr); + const diff = recipientBalanceFinal.sub(recipientBalanceInitial) + + expect(weibarTotinybar(diff)).to.equal(TRANSFER_AMOUNT); + expect(recipientBalanceInitial.lt(recipientBalanceFinal)).to.be.true; + }) + + it('should verify solidity functionality: .send', async function () { + const recipientBalanceInitial = await ethers.provider.getBalance(recipientAddr); + + const tx = await contract.sendTo(recipientAddr, TRANSFER_AMOUNT); + await tx.wait(); + + const recipientBalanceFinal = await ethers.provider.getBalance(recipientAddr); + const diff = recipientBalanceFinal.sub(recipientBalanceInitial) + + expect(weibarTotinybar(diff)).to.equal(TRANSFER_AMOUNT); + expect(recipientBalanceInitial.lt(recipientBalanceFinal)).to.be.true; + }) + + it('should verify solidity functionality: .call', async function () { + const recipientBalanceInitial = await ethers.provider.getBalance(recipientAddr); + + const tx = await contract.callAddr(recipientAddr, TRANSFER_AMOUNT); + await tx.wait(); + + const recipientBalanceFinal = await ethers.provider.getBalance(recipientAddr); + const diff = recipientBalanceFinal.sub(recipientBalanceInitial) + + expect(weibarTotinybar(diff)).to.equal(TRANSFER_AMOUNT); + expect(recipientBalanceInitial.lt(recipientBalanceFinal)).to.be.true; + }) + + it('should verify solidity functionality: .call -> with function signature', async function () { + const resTx = await contract.callAddrWithSig(recipientAddr, TRANSFER_AMOUNT, "getMessageValue()"); + const receipt = await resTx.wait(); + const data = receipt.events[0].data; + const value = BigInt(data); + + expect(value).to.equal(TRANSFER_AMOUNT); + }) + + it('should verify solidity functionality: .delegatecall', async function () { + const MESSAGE_FROM_ADDRESS = "Hello World from AddressContract!"; + const resTx = await contract.delegate(recipientAddr, "helloWorldMessage()"); + const receipt = await resTx.wait(); + const message = receipt.events[0].args[0]; + + expect(message).to.equal(MESSAGE_FROM_ADDRESS); + }) + + it('should verify solidity functionality: .staticcall', async function () { + const MY_NUMBER = 5; + const resTx = await contract.staticCall(recipientAddr, "getNumber()"); + const receipt = await resTx.wait(); + const result = receipt.events[0].args[1]; + const myNumber = BigInt(result); + + expect(myNumber).to.equal(MY_NUMBER); + }) + + it('should verify solidity functionality: .staticcall -> Try to set state', async function () { + try { + const resTx = await contract.staticCallSet(recipientAddr, "setNumber(uint number)", 10); + await resTx.wait(); + } catch (error) { + expect(error.code).to.equal('CALL_EXCEPTION'); + } + }) +})