Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
14 commits
Select commit Hold shift + click to select a range
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
2 changes: 1 addition & 1 deletion GOVERNANCE_TESTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ We will:
- `HuffyTimelock`
- `HuffyGovernor` (wired to the Timelock and HTK)
- `ParameterStore` (owned by Timelock)
- `PairWhitelist` (owned by Timelock)
- `PairWhitelist` (managed directly by DAO admin)
- `HTK` Votes token address (ERC20Votes-compatible) with voting power delegated

If you need to deploy the stack, see `script/Governor.s.sol` for a reference deployment flow.
Expand Down
26 changes: 16 additions & 10 deletions INTEGRATION_TESTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ RPC_URL=127.0.0.1:8545
USDC_TOKEN_ADDRESS=0x5bf5b11053e734690269C6B9D438F8C9d48F528A
HTK_TOKEN_ADDRESS=0x3347B4d90ebe72BeFb30444C9966B2B990aE9FcB
SAUCERSWAP_ROUTER=0x3aAde2dCD2Df6a8cAc689EE797591b2913658659
SWAP_ADAPTER_ADDRESS=0xSwapAdapter
MOCK_DAO_ADDRESS=0x1f10F3Ba7ACB61b2F50B9d6DdCf91a6f787C0E82
DAO_ADMIN_ADDRESS=0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266
RELAY_ADDRESS=0xb9bEECD1A582768711dE1EE7B0A1d582D9d72a6C
Expand All @@ -31,6 +32,11 @@ MAX_SLIPPAGE_BPS=500 # 5%
TRADE_COOLDOWN_SEC=60 # 60 seconds

DEADLINE=1918370747

# Adapter paths (bytes-encoded routes for the swap adapter)
USDC_TO_HTK_PATH=0xYourEncodedPathHere
HTK_TO_USDC_PATH=0xYourReversePathHere
USDC_TO_USDC_PATH=0xLoopbackPathForTesting
```

```shell
Expand Down Expand Up @@ -225,7 +231,7 @@ cast call $TREASURY_ADDRESS "getBalance(address)" $HTK_TOKEN_ADDRESS --rpc-url $
# Step 3: Propose (relay) and Execute (treasury) swap 1000 USDC (6 decimals) -> HTK (18 decimals)
# amountIn = 1000 * 10^6 = 1000000000 (1000 USDC)
# amountOutMin = 1900 * 10^18 = 1900000000000000000000 (1900 HTK with 5% slippage)
cast send $RELAY_ADDRESS "proposeSwap(address,address,uint256,uint256,uint256)" $USDC_TOKEN_ADDRESS $HTK_TOKEN_ADDRESS 1000000000 1900000000000000000000 $DEADLINE --rpc-url $RPC_URL --private-key $PRIVATE_KEY
cast send $RELAY_ADDRESS "proposeSwap(address,address,bytes,uint256,uint256,uint256)" $USDC_TOKEN_ADDRESS $HTK_TOKEN_ADDRESS $USDC_TO_HTK_PATH 1000000000 1900000000000000000000 $DEADLINE --rpc-url $RPC_URL --private-key $PRIVATE_KEY
```

```shell
Expand Down Expand Up @@ -255,7 +261,7 @@ cast call $TREASURY_ADDRESS "getBalance(address)" $HTK_TOKEN_ADDRESS --rpc-url $
# Step 2: Execute buyback-and-burn 500 USDC -> HTK (burn)
# amountIn = 500 * 10^6 = 500000000 (500 USDC)
# amountOutMin = 950 * 10^18 = 950000000000000000000 (950 HTK with 5% slippage)
cast send $RELAY_ADDRESS "proposeBuybackAndBurn(address,uint256,uint256,uint256)" $USDC_TOKEN_ADDRESS 500000000 950000000000000000000 $DEADLINE --rpc-url $RPC_URL --private-key $PRIVATE_KEY
cast send $RELAY_ADDRESS "proposeBuybackAndBurn(address,bytes,uint256,uint256,uint256)" $USDC_TOKEN_ADDRESS $USDC_TO_HTK_PATH 500000000 950000000000000000000 $DEADLINE --rpc-url $RPC_URL --private-key $PRIVATE_KEY
```
```shell
# Step 3: Check balances after buyback (HTK should be burned – sent to 0xdead)
Expand Down Expand Up @@ -283,12 +289,12 @@ cast call $RELAY_ADDRESS "getMaxAllowedTradeAmount(address)" $HTK_TOKEN_ADDRESS
# Option A (respect 10% cap): swap 200 HTK
# amountIn = 200 * 10^18 = 200000000000000000000 (200 HTK)
# With rate 1 HTK = 0.5 USDC, expected = 100 USDC; with 5% slippage, min = 95 USDC
cast send $RELAY_ADDRESS "proposeSwap(address,address,uint256,uint256,uint256)" $HTK_TOKEN_ADDRESS $USDC_TOKEN_ADDRESS 200000000000000000000 95000000 $DEADLINE --rpc-url $RPC_URL --private-key $PRIVATE_KEY
cast send $RELAY_ADDRESS "proposeSwap(address,address,bytes,uint256,uint256,uint256)" $HTK_TOKEN_ADDRESS $USDC_TOKEN_ADDRESS $HTK_TO_USDC_PATH 200000000000000000000 95000000 $DEADLINE --rpc-url $RPC_URL --private-key $PRIVATE_KEY

# Option B (if you want to trade 1000 HTK): temporarily raise the cap via DAO to 50%
# cast send $RELAY_ADDRESS "setMaxTradeBps(uint256)" 5000 --rpc-url $RPC_URL --private-key $PRIVATE_KEY
# Then you can use the original 1000 HTK example:
# cast send $RELAY_ADDRESS "proposeSwap(address,address,uint256,uint256,uint256)" $HTK_TOKEN_ADDRESS $USDC_TOKEN_ADDRESS 1000000000000000000000 475000000 $DEADLINE --rpc-url $RPC_URL --private-key $PRIVATE_KEY
# cast send $RELAY_ADDRESS "proposeSwap(address,address,bytes,uint256,uint256,uint256)" $HTK_TOKEN_ADDRESS $USDC_TOKEN_ADDRESS $HTK_TO_USDC_PATH 1000000000000000000000 475000000 $DEADLINE --rpc-url $RPC_URL --private-key $PRIVATE_KEY

# Check balances
echo "HTK balance:"
Expand Down Expand Up @@ -395,7 +401,7 @@ cast send $PAIR_WHITELIST_ADDRESS "removePair(address,address)" $USDC_TOKEN_ADDR
cast call $PAIR_WHITELIST_ADDRESS "isPairWhitelisted(address,address)" $USDC_TOKEN_ADDRESS $HTK_TOKEN_ADDRESS --rpc-url $RPC_URL

# Attempt swap on blacklisted pair (should fail)
cast send $RELAY_ADDRESS "proposeSwap(address,address,uint256,uint256,uint256)" $USDC_TOKEN_ADDRESS $HTK_TOKEN_ADDRESS 1000000000 1900000000000000000000 $DEADLINE --rpc-url $RPC_URL --private-key $PRIVATE_KEY
cast send $RELAY_ADDRESS "proposeSwap(address,address,bytes,uint256,uint256,uint256)" $USDC_TOKEN_ADDRESS $HTK_TOKEN_ADDRESS $USDC_TO_HTK_PATH 1000000000 1900000000000000000000 $DEADLINE --rpc-url $RPC_URL --private-key $PRIVATE_KEY

# Restore pair to whitelist
cast send $PAIR_WHITELIST_ADDRESS "addPair(address,address)" $USDC_TOKEN_ADDRESS $HTK_TOKEN_ADDRESS --rpc-url $RPC_URL --private-key $PRIVATE_KEY
Expand All @@ -419,22 +425,22 @@ cast send $PAIR_WHITELIST_ADDRESS "addPair(address,address)" $HTK_TOKEN_ADDRESS
```shell
# Test 1: Attempt trade without TRADER_ROLE (should fail)
UNAUTHORIZED_USER=0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC
cast send $RELAY_ADDRESS "proposeSwap(address,address,uint256,uint256,uint256)" $USDC_TOKEN_ADDRESS $HTK_TOKEN_ADDRESS 1000000000 1900000000000000000000 $DEADLINE --rpc-url $RPC_URL --private-key 0x5de4111afa1a4b94908f83103eb1f1706367c2e68ca870fc3fb9a804cdab365a
cast send $RELAY_ADDRESS "proposeSwap(address,address,bytes,uint256,uint256,uint256)" $USDC_TOKEN_ADDRESS $HTK_TOKEN_ADDRESS $USDC_TO_HTK_PATH 1000000000 1900000000000000000000 $DEADLINE --rpc-url $RPC_URL --private-key 0x5de4111afa1a4b94908f83103eb1f1706367c2e68ca870fc3fb9a804cdab365a
# Expected: AccessControlUnauthorizedAccount

# Test 2: Attempt swap on non-whitelisted pair
cast send $RELAY_ADDRESS "proposeSwap(address,address,uint256,uint256,uint256)" $USDC_TOKEN_ADDRESS $USDC_TOKEN_ADDRESS 1000000000 1000000000 $DEADLINE --rpc-url $RPC_URL --private-key $PRIVATE_KEY
cast send $RELAY_ADDRESS "proposeSwap(address,address,bytes,uint256,uint256,uint256)" $USDC_TOKEN_ADDRESS $USDC_TOKEN_ADDRESS $USDC_TO_USDC_PATH 1000000000 1000000000 $DEADLINE --rpc-url $RPC_URL --private-key $PRIVATE_KEY
# Expected: Relay: Pair not whitelisted

# Test 3: Attempt trade before cooldown
cast send $RELAY_ADDRESS "proposeSwap(address,address,uint256,uint256,uint256)" $USDC_TOKEN_ADDRESS $HTK_TOKEN_ADDRESS 1000000000 1900000000000000000000 $DEADLINE --rpc-url $RPC_URL --private-key $PRIVATE_KEY
cast send $RELAY_ADDRESS "proposeSwap(address,address,bytes,uint256,uint256,uint256)" $USDC_TOKEN_ADDRESS $HTK_TOKEN_ADDRESS $USDC_TO_HTK_PATH 1000000000 1900000000000000000000 $DEADLINE --rpc-url $RPC_URL --private-key $PRIVATE_KEY
# Immediately after:
cast send $RELAY_ADDRESS "proposeSwap(address,address,uint256,uint256,uint256)" $HTK_TOKEN_ADDRESS $USDC_TOKEN_ADDRESS 1000000000000000000000 475000000 $DEADLINE --rpc-url $RPC_URL --private-key $PRIVATE_KEY
cast send $RELAY_ADDRESS "proposeSwap(address,address,bytes,uint256,uint256,uint256)" $HTK_TOKEN_ADDRESS $USDC_TOKEN_ADDRESS $HTK_TO_USDC_PATH 1000000000000000000000 475000000 $DEADLINE --rpc-url $RPC_URL --private-key $PRIVATE_KEY
# Expected: Relay: Cooldown active

# Test 4: Attempt to exceed maxTradeBps
# If Treasury has 100,000 USDC, max trade = 10% = 10,000 USDC
cast send $RELAY_ADDRESS "proposeSwap(address,address,uint256,uint256,uint256)" $USDC_TOKEN_ADDRESS $HTK_TOKEN_ADDRESS 20000000000 1 $DEADLINE --rpc-url $RPC_URL --private-key $PRIVATE_KEY
cast send $RELAY_ADDRESS "proposeSwap(address,address,bytes,uint256,uint256,uint256)" $USDC_TOKEN_ADDRESS $HTK_TOKEN_ADDRESS $USDC_TO_HTK_PATH 20000000000 1 $DEADLINE --rpc-url $RPC_URL --private-key $PRIVATE_KEY
# Expected: Relay: Trade amount exceeds limit
```

Expand Down
24 changes: 23 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,29 @@ forge script script/SaucerswapMock.s.sol:SaucerswapMock \
```
source .env
```
`HTK_TOKEN_ADDRESS, SAUCERSWAP_ROUTER, DAO_ADMIN_ADDRESS, RELAY_ADDRESS`
`SAUCERSWAP_ROUTER, WHBAR_TOKEN_ADDRESS`
```bash
forge script script/SwapRouterProxyHedera.s.sol:DeploySwapRouterProxyHedera \
--rpc-url $HEDERA_RPC_URL \
--private-key $PRIVATE_KEY \
--broadcast
```
---
```
source .env
```
`SWAP_ROUTER_PROXY_ADDRESS`
```bash
forge script script/SaucerswapAdapter.s.sol:DeploySaucerswapAdapter \
--rpc-url $HEDERA_RPC_URL \
--private-key $PRIVATE_KEY \
--broadcast
```
---
```
source .env
```
`SWAP_ADAPTER_ADDRESS`
```bash
forge script script/Treasury.s.sol:DeployTreasury \
--rpc-url $HEDERA_RPC_URL \
Expand Down
8 changes: 6 additions & 2 deletions RELAY_TESTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -142,9 +142,10 @@ Only authorized traders can submit trades:

```bash
# From trader account (HuffyPuppet)
cast send $RELAY_ADDRESS "proposeSwap(address,address,uint256,uint256,uint256)" \
cast send $RELAY_ADDRESS "proposeSwap(address,address,bytes,uint256,uint256,uint256)" \
$USDC_TOKEN_ADDRESS \
$USDT_TOKEN_ADDRESS \
$USDC_TO_USDT_PATH \
100000000 \
95000000 \
$DEADLINE \
Expand All @@ -155,6 +156,7 @@ cast send $RELAY_ADDRESS "proposeSwap(address,address,uint256,uint256,uint256)"
Parameters:
- tokenIn: Input token address
- tokenOut: Output token address
- path: Adapter-specific encoded bytes path describing the swap route
- amountIn: Amount to swap (e.g., 100 USDC = 100000000 with 6 decimals)
- minAmountOut: Minimum expected output (accounting for slippage)
- deadline: UNIX timestamp
Expand All @@ -163,8 +165,9 @@ Parameters:

```bash
# From trader account
cast send $RELAY_ADDRESS "proposeBuybackAndBurn(address,uint256,uint256,uint256)" \
cast send $RELAY_ADDRESS "proposeBuybackAndBurn(address,bytes,uint256,uint256,uint256)" \
$USDC_TOKEN_ADDRESS \
$USDC_TO_HTK_PATH \
100000000 \
190000000000000000000 \
$DEADLINE \
Expand All @@ -174,6 +177,7 @@ cast send $RELAY_ADDRESS "proposeBuybackAndBurn(address,uint256,uint256,uint256)

Parameters:
- tokenIn: Input token address (e.g., USDC)
- path: Adapter-specific bytes path for the trade
- amountIn: Amount to swap (e.g., 100 USDC)
- minAmountOut: Minimum HTK expected (e.g., 190 HTK with 18 decimals)
- deadline: UNIX timestamp
Expand Down
22 changes: 14 additions & 8 deletions TREASURY_TESTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,18 @@
This project includes a Treasury contract that can hold tokens, execute buyback-and-burn operations via the Saucerswap router, perform generic token swaps, and allow DAO-controlled withdrawals.

Important context from the code:
- Treasury constructor: `Treasury(address htkToken, address saucerswapRouter, address daoAdmin, address relay)`
- Treasury constructor: `Treasury(address htkToken, address swapAdapter, address daoAdmin, address relay)`
- Roles:
- `DEFAULT_ADMIN_ROLE` and `DAO_ROLE` are given to `daoAdmin` at deploy time.
- `RELAY_ROLE` is given to `relay` at deploy time; only the relay can call swap and buyback.
- Router interface: uses `swapExactTokensForTokens` as per Saucerswap.
- Swaps route directly through an `ISwapAdapter` set on Treasury; the DAO can update the adapter via `setAdapter(address)`.
- Mocks: `MockERC20`, `MockSaucerswapRouter` (with settable exchange rates), `MockDAO` (acts as DAO admin), and `MockRelay` (to invoke Treasury methods).

To change adapters in production, the DAO calls `setAdapter(address)` on Treasury:
```
cast send $TREASURY_ADDRESS "setAdapter(address)" $NEW_ADAPTER --rpc-url $RPC_URL --private-key $DAO_ADMIN_PRIVATE_KEY
```

Important script roles:
- script/DeployMocks.s.sol: Deploys a full testing environment on testnet, including mocks AND a Treasury instance wired to the mock router. Use this to prepare contracts and addresses for end-to-end testing.
- script/Treasury.s.sol: Production deployment script for the real Treasury on testnet/mainnet with your real token/router/admin/relay settings.
Expand Down Expand Up @@ -119,30 +124,31 @@ Only an account with `RELAY_ROLE` can call `executeBuybackAndBurn`. You may invo

Direct call (caller must have `RELAY_ROLE`):
```
# Swap 100 USDC for HTK and burn the HTK received
cast send $TREASURY_ADDRESS "executeBuybackAndBurn(address,uint256,uint256,uint256)" $USDC_TOKEN_ADDRESS 100000000 0 $DEADLINE --rpc-url $RPC_URL --private-key $PRIVATE_KEY
# Swap 100 USDC for HTK and burn the HTK received (provide an encoded swap path)
cast send $TREASURY_ADDRESS "executeBuybackAndBurn(address,bytes,uint256,uint256,uint256)" $USDC_TOKEN_ADDRESS $USDC_TO_HTK_PATH 100000000 0 $DEADLINE --rpc-url $RPC_URL --private-key $PRIVATE_KEY
```
Via MockRelay (recommended during testing):
```
cast send $RELAY "executeBuybackAndBurn(address,uint256,uint256,uint256)" $USDC_TOKEN_ADDRESS 100000000 0 DEADLINE --rpc-url $RPC_URL --private-key $PRIVATE_KEY
cast send $RELAY "executeBuybackAndBurn(address,bytes,uint256,uint256,uint256)" $USDC_TOKEN_ADDRESS $USDC_TO_HTK_PATH 100000000 0 DEADLINE --rpc-url $RPC_URL --private-key $PRIVATE_KEY
```
Notes:
- Ensure the Treasury holds enough `tokenIn` (e.g., USDC) before calling.
- `$USDC_TO_HTK_PATH` should contain the adapter-specific bytes-encoded path for the swap route.
- `amountOutMin` can be set to 0 for testing, or a slippage-protected minimum.
- The burn is implemented by transferring HTK to the `0xdead` address and emits `Burned(amount, initiator, timestamp)`.

### D) Generic trade-swap without burning (Relay only)

Use `executeSwap(tokenIn, tokenOut, amountIn, amountOutMin, deadline)` to swap and keep proceeds in the Treasury.
Use `executeSwap(tokenIn, tokenOut, path, amountIn, amountOutMin, deadline)` to swap and keep proceeds in the Treasury.

Direct call on Treasury (requires `RELAY_ROLE`):
```
# Example: swap USDC -> HTK
cast send $TREASURY_ADDRESS "executeSwap(address,address,uint256,uint256,uint256)" $USDC_TOKEN_ADDRESS $HTK_TOKEN_ADDRESS 100000000 0 $DEADLINE --rpc-url $RPC_URL --private-key $PRIVATE_KEY
cast send $TREASURY_ADDRESS "executeSwap(address,address,bytes,uint256,uint256,uint256)" $USDC_TOKEN_ADDRESS $HTK_TOKEN_ADDRESS $USDC_TO_HTK_PATH 100000000 0 $DEADLINE --rpc-url $RPC_URL --private-key $PRIVATE_KEY
```
Via MockRelay:
```
cast send $RELAY_ADDRESS "executeSwap(address,address,uint256,uint256,uint256)" $USDC_TOKEN_ADDRESS $HTK_TOKEN_ADDRESS 100000000 0 $DEADLINE --rpc-url $RPC_URL --private-key $PRIVATE_KEY
cast send $RELAY_ADDRESS "executeSwap(address,address,bytes,uint256,uint256,uint256)" $USDC_TOKEN_ADDRESS $HTK_TOKEN_ADDRESS $USDC_TO_HTK_PATH 100000000 0 $DEADLINE --rpc-url $RPC_URL --private-key $PRIVATE_KEY
```
Check the Treasury's balances after the swap:
```
Expand Down
4 changes: 2 additions & 2 deletions script/DAOmock.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,14 @@ contract MockDAOScript is Script {
MockDAO mockDao = new MockDAO();
console.log("Mock DAO:", address(mockDao));

address treasuryAddress = vm.envAddress("TREASURY_ADDRESS");
address payable treasuryAddress = payable(vm.envAddress("TREASURY_ADDRESS"));
Treasury treasury = Treasury(treasuryAddress);

treasury.grantRole(treasury.DEFAULT_ADMIN_ROLE(), address(mockDao));

address relay = vm.envOr("RELAY_ADDRESS", msg.sender);

mockDao.setTreasury(address(treasury));
mockDao.setTreasury(treasuryAddress);
mockDao.updateRelay(msg.sender, relay);

vm.stopBroadcast();
Expand Down
105 changes: 0 additions & 105 deletions script/Governor.s.sol

This file was deleted.

2 changes: 1 addition & 1 deletion script/Relay.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {Relay} from "../src/Relay.sol";
contract DeployRelay is Script {
function run() external {
address pairWhitelist = vm.envAddress("PAIR_WHITELIST_ADDRESS");
address treasury = vm.envAddress("TREASURY_ADDRESS");
address payable treasury = payable(vm.envAddress("TREASURY_ADDRESS"));
address saucerswapRouter = vm.envAddress("SAUCERSWAP_ROUTER");
address daoAdmin = vm.envOr("DAO_ADMIN_ADDRESS", msg.sender);

Expand Down
Loading
Loading