-
Notifications
You must be signed in to change notification settings - Fork 54
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: added TransactionInfo solidity example contract
Signed-off-by: Logan Nguyen <[email protected]>
- Loading branch information
1 parent
1b45c1e
commit bda3cba
Showing
2 changed files
with
239 additions
and
0 deletions.
There are no files selected for viewing
75 changes: 75 additions & 0 deletions
75
contracts/solidity/yul/transaction-information/TransactionInfo.sol
This file contains 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,75 @@ | ||
// SPDX-License-Identifier: Apache-2.0 | ||
pragma solidity ^0.8.20; | ||
|
||
contract TransactionInfo { | ||
|
||
constructor() payable {} | ||
|
||
/// gas still available to execution | ||
function getGasLeft() external view returns (uint256 result) { | ||
assembly{ | ||
result := gas() | ||
} | ||
} | ||
|
||
/// address of the current contract / execution context | ||
function getContractAddress() external view returns (address addr) { | ||
assembly { | ||
addr := address() | ||
} | ||
} | ||
|
||
/// get wei balance at address a | ||
function getBalance(address a) external view returns (uint256 bal) { | ||
assembly { | ||
bal := balance(a) | ||
} | ||
} | ||
|
||
/// get self balance - equivalent to balance(address()), but cheaper | ||
function getSelfBalance() external view returns (uint256 bal) { | ||
assembly { | ||
bal := selfbalance() | ||
} | ||
} | ||
|
||
/// get call sender | ||
function getMsgCaller() external view returns (address msgCaller) { | ||
assembly { | ||
msgCaller := caller() | ||
} | ||
} | ||
|
||
/// get wei sent together with the current call | ||
event CallValue(uint256 callBalance); | ||
function getCallValue() external payable { | ||
uint256 callBalance; | ||
assembly { | ||
callBalance := callvalue() | ||
} | ||
emit CallValue(callBalance); | ||
} | ||
|
||
/// call msg.data starting from position p (32 bytes) | ||
/// msg.data is a byte array that contains the function arguments encoded according to the function's signature. | ||
function getCallDataLoad(uint256 p) external pure returns (bytes32 data) { | ||
assembly { | ||
data := calldataload(p) | ||
} | ||
} | ||
|
||
/// size of call data in bytes | ||
function getCallDataSize() external pure returns (uint256 datasize) { | ||
assembly { | ||
datasize := calldatasize() | ||
} | ||
} | ||
|
||
/// calldatacopy(t, f, s) - copy `s` bytes from calldata at position `f` to memory at position `t` | ||
function callDataCopier(uint256 t, uint256 f, uint256 s) external pure returns (bytes32 data) { | ||
assembly { | ||
calldatacopy(t, f, s) | ||
data := mload(t) | ||
} | ||
} | ||
} |
164 changes: 164 additions & 0 deletions
164
test/solidity/yul/transaction-information/TransactionInfo.js
This file contains 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,164 @@ | ||
/*- | ||
* | ||
* Hedera Smart Contracts | ||
* | ||
* Copyright (C) 2023 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') | ||
|
||
describe('@solidityequiv5 TransactionInfo Tests', () => { | ||
let transactionInfoContract, signers | ||
const GASLIMIT = 1000000 | ||
const INITIAL_BALANCE = 30000000000 | ||
const tinybarToWeibarCoef = 10_000_000_000 | ||
|
||
before(async () => { | ||
signers = await ethers.getSigners() | ||
const transactionInfoContractFactory = await ethers.getContractFactory( | ||
'TransactionInfo' | ||
) | ||
transactionInfoContract = await transactionInfoContractFactory.deploy({ | ||
value: INITIAL_BALANCE, | ||
gasLimit: GASLIMIT, | ||
}) | ||
}) | ||
|
||
it('Should deploy with a call value', async () => { | ||
const intialBalance = await ethers.provider.getBalance( | ||
transactionInfoContract.address | ||
) | ||
|
||
expect(intialBalance).to.eq(INITIAL_BALANCE) | ||
}) | ||
|
||
it('Should get the gas left', async () => { | ||
const expectedGas = 9000132 | ||
const result = await transactionInfoContract.getGasLeft() | ||
|
||
expect(result).to.eq(expectedGas) | ||
}) | ||
|
||
it('Should get contract address', async () => { | ||
const expectedContractAddress = transactionInfoContract.address | ||
const result = await transactionInfoContract.getContractAddress() | ||
|
||
expect(result).to.eq(expectedContractAddress) | ||
}) | ||
|
||
it('Should get contract balance', async () => { | ||
const expectedSignerABalance = Math.round( | ||
(await signers[0].getBalance()) / tinybarToWeibarCoef | ||
) | ||
|
||
const result = await transactionInfoContract.getBalance( | ||
await signers[0].getAddress() | ||
) | ||
|
||
expect(result).to.eq(expectedSignerABalance) | ||
}) | ||
|
||
it('Should get self balance', async () => { | ||
const expectedSelfBalance = Math.round( | ||
INITIAL_BALANCE / tinybarToWeibarCoef | ||
) | ||
const result = await transactionInfoContract.getSelfBalance() | ||
|
||
expect(result).to.eq(expectedSelfBalance) | ||
}) | ||
|
||
it('Should get message caller', async () => { | ||
const expectedMessageCaller = signers[0].address | ||
|
||
const result = await transactionInfoContract.getMsgCaller() | ||
|
||
expect(result).to.eq(expectedMessageCaller) | ||
}) | ||
|
||
it('Should get message call value', async () => { | ||
const expectedValue = 10_000_000_000 | ||
|
||
const transaction = await transactionInfoContract.getCallValue({ | ||
value: expectedValue, | ||
}) | ||
const receipt = await transaction.wait() | ||
|
||
const event = receipt.events.map((e) => e.event === 'CallValue' && e)[0] | ||
|
||
const [messageValue] = event.args | ||
|
||
expect(messageValue).to.eq(expectedValue / tinybarToWeibarCoef) | ||
}) | ||
|
||
it('Should get message call data', async () => { | ||
const index = 2 | ||
const functionSig = 'getCallDataLoad(uint256)' | ||
const callData = transactionInfoContract.interface | ||
.encodeFunctionData(functionSig, [index]) | ||
.replace('0x', '') | ||
|
||
// @notice since transactionInfoContract.getCallDataLoad() returns the msg.calldata from memory offset `index`, | ||
// `bytes32CallData` also needs to dynamically truncate itself based on `index` | ||
const expectedBytes32CallData = | ||
`0x` + callData.slice(index * 2, 64 + index * 2) | ||
|
||
const result = await transactionInfoContract.getCallDataLoad(index) | ||
|
||
expect(result).to.eq(expectedBytes32CallData) | ||
}) | ||
|
||
it('Should get the size of message call data', async () => { | ||
const messagecallData = await transactionInfoContract.getCallDataLoad(0) | ||
const callDataBytesArraay = ethers.utils.arrayify(messagecallData) | ||
const significantBytesLength = callDataBytesArraay.reduce( | ||
(length, byte) => { | ||
if (byte !== 0) { | ||
return (length += 1) | ||
} else { | ||
return length | ||
} | ||
}, | ||
0 | ||
) | ||
|
||
const result = await transactionInfoContract.getCallDataSize() | ||
|
||
expect(result).to.eq(significantBytesLength) | ||
}) | ||
|
||
it('Should copy message call data to memory', async () => { | ||
const dataPosF = 0 | ||
const memPosT = 0x20 | ||
const bytesAmountS = 4 // max amount | ||
const functionSig = 'callDataCopier(uint256, uint256, uint256)' | ||
|
||
const messageCallData = transactionInfoContract.interface | ||
.encodeFunctionData(functionSig, [memPosT, dataPosF, bytesAmountS]) | ||
.replace('0x', '') | ||
|
||
const bytes32MessageCallData = | ||
'0x' + messageCallData.slice(dataPosF * 2, 64 + dataPosF * 2) | ||
|
||
const result = await transactionInfoContract.callDataCopier( | ||
memPosT, | ||
dataPosF, | ||
bytesAmountS | ||
) | ||
|
||
expect(result).to.eq(bytes32MessageCallData) | ||
}) | ||
}) |