Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 10 additions & 23 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -195,52 +195,39 @@ For example
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.0;

import {Script, console} from "forge-std/Script.sol";
import {Script} from "forge-std/Script.sol";
import {HTS_ADDRESS} from "hedera-forking/HtsSystemContract.sol";
import {IHederaTokenService} from "hedera-forking/IHederaTokenService.sol";
import {HederaResponseCodes} from "hedera-forking/HederaResponseCodes.sol";
import {Hsc} from "hedera-forking/Hsc.sol";

/**
* Given how Foundry script works, the flag `--skip-simulation` is necessary.
* For example
*
* forge script scripts/CreateToken.s.sol -vvv --rpc-url testnet --skip-simulation --broadcast
* forge script CreateTokenScript --rpc-url testnet --broadcast --skip-simulation
*/
contract CreateTokenScript is Script {
uint256 PRIVATE_KEY = vm.envUint("PRIVATE_KEY");

function run() external returns (int64 responseCode, address tokenAddress) {
function run() external returns (address signer, int64 responseCode, address tokenAddress) {
Hsc.htsSetup();

address signer = vm.addr(PRIVATE_KEY);
console.log("Signer address %s", signer);

vm.startBroadcast(PRIVATE_KEY);
uint256 PRIVATE_KEY = vm.envUint("PRIVATE_KEY");
signer = vm.addr(PRIVATE_KEY);

IHederaTokenService.KeyValue memory keyValue;
keyValue.inheritAccountKey = true;

IHederaTokenService.TokenKey[] memory keys = new IHederaTokenService.TokenKey[](1);
keys[0] = IHederaTokenService.TokenKey(1, keyValue);

IHederaTokenService.Expiry memory expiry;
expiry.second = 0;
expiry.autoRenewAccount = signer;
expiry.autoRenewPeriod = 8000000;

IHederaTokenService.HederaToken memory token;
token.name = "HTS Token Example Created with Foundry";
token.symbol = "FDRY";
token.treasury = signer;
token.memo = "This HTS Token was created using `forge script` together with HTS emulation";
token.tokenKeys = keys;
token.expiry = expiry;
token.tokenKeys = new IHederaTokenService.TokenKey[](2);
token.tokenKeys[0] = IHederaTokenService.TokenKey(0x1, keyValue); // Admin Key
token.tokenKeys[1] = IHederaTokenService.TokenKey(0x10, keyValue); // Supply Key enables minting
token.expiry = IHederaTokenService.Expiry(0, signer, 8000000);

vm.startBroadcast(PRIVATE_KEY);
(responseCode, tokenAddress) = IHederaTokenService(HTS_ADDRESS).createFungibleToken{value: 10 ether}(token, 10000, 4);
console.log("Response code %d", int(responseCode));
console.log("Token address %s", tokenAddress);

vm.stopBroadcast();
}
}
Expand Down
14 changes: 7 additions & 7 deletions contracts/HtsSystemContract.sol
Original file line number Diff line number Diff line change
Expand Up @@ -584,14 +584,14 @@ contract HtsSystemContract is IHederaTokenService {
if (selector == this.associateToken.selector) {
require(msg.data.length >= 48, "associateToken: Not enough calldata");
address account = address(bytes20(msg.data[40:60]));
bytes32 slot = _isAssociatedSlot(account);
bytes32 slot = _isAssociatedSlot(account, false);
assembly { sstore(slot, true) }
return abi.encode(HederaResponseCodes.SUCCESS);
}
if (selector == this.dissociateToken.selector) {
require(msg.data.length >= 48, "dissociateToken: Not enough calldata");
address account = address(bytes20(msg.data[40:60]));
bytes32 slot = _isAssociatedSlot(account);
bytes32 slot = _isAssociatedSlot(account, false);
assembly { sstore(slot, false) }
return abi.encode(HederaResponseCodes.SUCCESS);
}
Expand Down Expand Up @@ -797,17 +797,17 @@ contract HtsSystemContract is IHederaTokenService {

function _redirectForHRC719(bytes4 selector) private returns (bytes memory) {
if (selector == IHRC719.associate.selector) {
bytes32 slot = _isAssociatedSlot(msg.sender);
bytes32 slot = _isAssociatedSlot(msg.sender, false);
assembly { sstore(slot, true) }
return abi.encode(true);
}
if (selector == IHRC719.dissociate.selector) {
bytes32 slot = _isAssociatedSlot(msg.sender);
bytes32 slot = _isAssociatedSlot(msg.sender, false);
assembly { sstore(slot, false) }
return abi.encode(true);
}
if (selector == IHRC719.isAssociated.selector) {
bytes32 slot = _isAssociatedSlot(msg.sender);
bytes32 slot = _isAssociatedSlot(msg.sender, true);
bool res;
assembly { res := sload(slot) }
return abi.encode(res);
Expand Down Expand Up @@ -892,11 +892,11 @@ contract HtsSystemContract is IHederaTokenService {
return bytes32(abi.encodePacked(selector, pad, spenderId, ownerId));
}

function _isAssociatedSlot(address account) internal virtual returns (bytes32) {
function _isAssociatedSlot(address account, bool revertIfNotExists) internal virtual returns (bytes32) {
bytes4 selector = IHRC719.isAssociated.selector;
uint192 pad = 0x0;
(uint32 accountId, bool exists) = HtsSystemContract(HTS_ADDRESS).getAccountId(account);
require(exists);
require(!revertIfNotExists || exists, "_isAssociatedSlot: account does not exist");
return bytes32(abi.encodePacked(selector, pad, accountId));
}

Expand Down
4 changes: 2 additions & 2 deletions contracts/HtsSystemContractJson.sol
Original file line number Diff line number Diff line change
Expand Up @@ -487,8 +487,8 @@ contract HtsSystemContractJson is HtsSystemContract {
return slot;
}

function _isAssociatedSlot(address account) internal override returns (bytes32) {
bytes32 slot = super._isAssociatedSlot(account);
function _isAssociatedSlot(address account, bool revertIfNotExists) internal override returns (bytes32) {
bytes32 slot = super._isAssociatedSlot(account, revertIfNotExists);
if (_shouldFetch(slot)) {
bool associated = mirrorNode().isAssociated(address(this), account);
_setValue(slot, bytes32(uint256(associated ? 1 : 0)));
Expand Down
47 changes: 47 additions & 0 deletions examples/foundry-hts/script/ApproveToken.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.0;

import {Script, VmSafe} from "forge-std/Script.sol";
import {IERC20} from "hedera-forking/IERC20.sol";
import {IHRC719} from "hedera-forking/IHRC719.sol";
import {Hsc} from "hedera-forking/Hsc.sol";

contract Approver {
function associate(address tokenAddress) external {
IHRC719(tokenAddress).associate();
}

function approve(address tokenAddress, address account, uint256 amount) external {
IERC20(tokenAddress).approve(account, amount);
}
}

/**
* Usage
*
* forge script ApproveTokenScript --sig "run(address)" $TOKEN_ADDR --rpc-url testnet --broadcast --skip-simulation --slow
*/
contract ApproveTokenScript is Script {
function run(address tokenAddress) external returns (Approver approver, address payable account) {
Hsc.htsSetup();

uint256 PRIVATE_KEY = vm.envUint("PRIVATE_KEY");

vm.startBroadcast(PRIVATE_KEY);
approver = new Approver();
vm.stopBroadcast();

vm.startBroadcast(PRIVATE_KEY);
Approver(approver).associate(tokenAddress);

IERC20(tokenAddress).transfer(address(approver), 50000);

VmSafe.Wallet memory wallet = vm.createWallet("wallet");
account = payable(wallet.addr);
account.transfer(1 ether); // 1 HBAR

Approver(approver).approve(tokenAddress, account, 400_0000);

vm.stopBroadcast();
}
}
33 changes: 10 additions & 23 deletions examples/foundry-hts/script/CreateToken.s.sol
Original file line number Diff line number Diff line change
@@ -1,52 +1,39 @@
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.0;

import {Script, console} from "forge-std/Script.sol";
import {Script} from "forge-std/Script.sol";
import {HTS_ADDRESS} from "hedera-forking/HtsSystemContract.sol";
import {IHederaTokenService} from "hedera-forking/IHederaTokenService.sol";
import {HederaResponseCodes} from "hedera-forking/HederaResponseCodes.sol";
import {Hsc} from "hedera-forking/Hsc.sol";

/**
* Given how Foundry script works, the flag `--skip-simulation` is necessary.
* For example
*
* forge script scripts/CreateToken.s.sol -vvv --rpc-url testnet --skip-simulation --broadcast
* forge script CreateTokenScript --rpc-url testnet --broadcast --skip-simulation
*/
contract CreateTokenScript is Script {
uint256 PRIVATE_KEY = vm.envUint("PRIVATE_KEY");

function run() external returns (int64 responseCode, address tokenAddress) {
function run() external returns (address signer, int64 responseCode, address tokenAddress) {
Hsc.htsSetup();

address signer = vm.addr(PRIVATE_KEY);
console.log("Signer address %s", signer);

vm.startBroadcast(PRIVATE_KEY);
uint256 PRIVATE_KEY = vm.envUint("PRIVATE_KEY");
signer = vm.addr(PRIVATE_KEY);

IHederaTokenService.KeyValue memory keyValue;
keyValue.inheritAccountKey = true;

IHederaTokenService.TokenKey[] memory keys = new IHederaTokenService.TokenKey[](1);
keys[0] = IHederaTokenService.TokenKey(1, keyValue);

IHederaTokenService.Expiry memory expiry;
expiry.second = 0;
expiry.autoRenewAccount = signer;
expiry.autoRenewPeriod = 8000000;

IHederaTokenService.HederaToken memory token;
token.name = "HTS Token Example Created with Foundry";
token.symbol = "FDRY";
token.treasury = signer;
token.memo = "This HTS Token was created using `forge script` together with HTS emulation";
token.tokenKeys = keys;
token.expiry = expiry;
token.tokenKeys = new IHederaTokenService.TokenKey[](2);
token.tokenKeys[0] = IHederaTokenService.TokenKey(0x1, keyValue); // Admin Key
token.tokenKeys[1] = IHederaTokenService.TokenKey(0x10, keyValue); // Supply Key enables minting
token.expiry = IHederaTokenService.Expiry(0, signer, 8000000);

vm.startBroadcast(PRIVATE_KEY);
(responseCode, tokenAddress) = IHederaTokenService(HTS_ADDRESS).createFungibleToken{value: 10 ether}(token, 10000, 4);
console.log("Response code %d", int(responseCode));
console.log("Token address %s", tokenAddress);

vm.stopBroadcast();
}
}
24 changes: 24 additions & 0 deletions examples/foundry-hts/script/MintToken.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.0;

import {Script} from "forge-std/Script.sol";
import {HTS_ADDRESS} from "hedera-forking/HtsSystemContract.sol";
import {IHederaTokenService} from "hedera-forking/IHederaTokenService.sol";
import {Hsc} from "hedera-forking/Hsc.sol";

/**
* Replace the variables `$TOKEN_ADDR` and `$AMOUNT` with the desired values.
*
* forge script MintTokenScript --sig "run(address,uint)" $TOKEN_ADDR $AMOUNT --rpc-url testnet --broadcast --skip-simulation
*/
contract MintTokenScript is Script {
function run(address tokenAddress, uint256 amount) external returns (int64 responseCode, int64 newTotalSupply) {
Hsc.htsSetup();

uint256 PRIVATE_KEY = vm.envUint("PRIVATE_KEY");

vm.startBroadcast(PRIVATE_KEY);
(responseCode, newTotalSupply,) = IHederaTokenService(HTS_ADDRESS).mintToken(tokenAddress, int64(int256(amount)), new bytes[](0));
vm.stopBroadcast();
}
}
Loading