Skip to content

Instantly share code, notes, and snippets.

@ndeto
Last active April 25, 2025 22:31
Show Gist options
  • Save ndeto/e6154c9c522cc7477e69db60ee3d1757 to your computer and use it in GitHub Desktop.
Save ndeto/e6154c9c522cc7477e69db60ee3d1757 to your computer and use it in GitHub Desktop.
Uniswap Assembly Contract (Huff)
// I extracted this logic from a suite of contracts I wrote in Huff meant to perform swaps as part of an AMM system.
// This is just provide an example of how your can write smart contracts at assembly level by simply manipulating the stack and calling opcodes.
// This is for learning purposes only
// Swap function
#define function swap() nonpayable returns ()
// These constants will be replaced by the actual values during deploy time
#define constant OWNER = 0x6dc63ab72c3b163f71ce602ea2727307cf9a40402cd20689e35f0aa0eb82b586
#define constant AMOUNT_IN = 0x23864fb08284051ca4c4decf63586b6c3f4263de1c22f8c7e77a135196a69007
#define constant EXPECTED_AMOUNT_OUT = 0x925b022ee8e701f539a353cbfd9a0852983f23bccf13eab86ec816d73003f94d
#define constant SWAP_FEE = 0x1caf8778049e0c13a1d5db49fd0aaf32a5573a00a294c90563550f0d4f404bfa
#define constant BASE_FEE = 0xe175b348864284ecdbf4c3c68afca185824661959253a859b48b92bd25b07331
#define constant BUY_TAX = 0x1c9e613b095883a828b3f55c9d2330ef03fc27465fccbe0e9b1beb40af543371
#define constant PAIR_ADDRESS = 0x4eab33021c15dc5f500948974f788b26434e076d83383a2437fc83a7614fc01b
#define constant ETH_ADDRESS = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2
// Define storage slots.
#define constant TOKEN_IN = 0x500
#define constant TOKEN_OUT = 0x520
#define constant TOKEN0 = 0x540
#define constant TOKEN1 = 0x560
#define constant TOKEN_IN_BALANCE = 0x580
#define macro BALANCE_OF_SENDER() = takes (1) returns (1) {
// Takes the token address on the stack and checks the balanceOf of the user
// [token address]
__RIGHTPAD(0x70a08231) callvalue mstore // FuncSig
[OWNER] 0x04 mstore // address for this contract
0x00 // [0x00, address]
0x24 // [0x24, 0x00, address]
callvalue // [callvalue, 0x24, 0x00, address]
0x00 // [0x00, callvalue, 0x24, 0x00, address]
swap4 // [address, callvalue, 0x24, 0x00, 0x00]
gas // [gas, address, callvalue, 0x24, 0x00, 0x00]
staticcall
returnBalance // move to store the balance if call was successful
jumpi
REVERT() // revert if call wasn't successful
returnBalance:
returndatasize 0x00 0x00 returndatacopy
0x00 mload
}
#define macro UNISWAP_V2_FLASHSWAP_CALLBACK() = takes (5) returns (0) {
// Starting stack
// [pair, senderAddress, tokenInAddress, pair, amountIn]
// Check call came from pair
caller // [msg.sender, pair, senderAddress, tokenInAddress, pair, amountIn]
eq // [0/1, senderAddress, tokenInAddress, pair, amountIn]
checkSender
jumpi // [senderAddress, tokenInAddress, pair, amountIn]
REVERT()
checkSender: // [senderAddress, tokenInAddress, pair, amountIn]
address // [ourContractAddress, senderAddress, tokenInAddress, pair, amountIn]
eq // [0/1, tokenInAddress, pair, amountIn]
makeTransfer
jumpi
makeTransfer: // [tokenInAddress, pair, amountIn]
[PAIR_ADDRESS]
SORT_TOKENS()
[TOKEN_IN] mload // [tokenInAddress, pair, amountIn]
BALANCE_OF_SENDER() // [tokenInAddress, pair, amountIn, senderBalance]
[TOKEN_IN_BALANCE] mstore // [pair, amountIn, senderBalance]
TRANSFER_FROM()
}
#define macro TRANSFER_FROM() = takes (4) returns (0) {
// Starting stack [owner tokenInAddress, pair, amountIn]
__RIGHTPAD(0x23b872dd) 0x100 mstore // [tokenInAddress, pair, amountIn]
[OWNER] 0x104 mstore // [senderAddress, tokenInAddress, pair, amountIn]
[PAIR_ADDRESS] 0x124 mstore // [pair, amountIn] Store tokenIn in a different location
[TOKEN_IN_BALANCE] mload 0x144 mstore // [amountIn, amountIn] Store amountIn in a different location
0x00 0x00 0x64 0x100
0x00
[TOKEN_IN] mload
gas
call
0x01
doneStep
jumpi
REVERT()
doneStep:
}
// Given a pair address, returns [alternatecoin, ETH]
#define macro SORT_TOKENS() = takes (1) returns (0) {
// [pairAddress]
dup1 // [pairAddress, pairAddress]
GET_TOKEN_ZERO() // [token0Address, pairAddress]
[TOKEN0] mstore // [pairAddress]
pop pop
GET_TOKEN_ONE() // [token1Address]
[TOKEN1] mstore // []
// Determine which token is ETH
[TOKEN0] mload // [token0Address]
[ETH_ADDRESS] // [ETH, token0Address]
eq
returnToken1AsShxtcoin
jumpi
returnToken0AsShxtcoin:
[TOKEN1] mload // [token1Address]
[TOKEN_IN] mstore // []
[TOKEN0] mload // [token0Address]
[TOKEN_OUT] mstore // [token0Address, token1Address]
returnToken1AsShxtcoin:
[TOKEN0] mload // [token1Address]
[TOKEN_IN] mstore // []
[TOKEN1] mload // [token0Address]
[TOKEN_OUT] mstore // [token0Address, token1Address]
done
jump
done:
}
#define macro GET_TOKEN_ONE() = takes (1) returns (1){
// [pairAddress]
__FUNC_SIG("token1()") 0x00 mstore
0x00 // [0x00, pairAddress]
0x00 // [0x00, 0x00, pairAddress]
0x04 // [0x04, 0x00, 0x00, pairAddress]
0x1c // [0x1c, 0x04, 0x00, 0x00, pairAddress]
0x00 calldataload // [0x00, 0x1c, 0x04, 0x00, 0x00, pairAddress]
swap5 // [pairAddress, 0x1c, 0x04, 0x00, 0x00, 0x00]
gas // [gas, pairAddress, 0x1c, 0x04, 0x00, 0x00, 0x00]
staticcall
returndatasize 0x00 0x00 returndatacopy //copy token0() results to 0x00 in memory
0x00 mload // Return the token 0 address
}
#define macro GET_TOKEN_ZERO() = takes (1) returns (1){
// [pairAddress]
__FUNC_SIG("token0()") 0x00 mstore
0x00 // [0x00, pairAddress]
0x00 // [0x00, 0x00, pairAddress]
0x04 // [0x04, 0x00, 0x00, pairAddress]
0x1c // [0x1c, 0x04, 0x00, 0x00, pairAddress]
0x00 calldataload // [0x00, 0x1c, 0x04, 0x00, 0x00, pairAddress]
swap5 // [pairAddress, 0x1c, 0x04, 0x00, 0x00, 0x00]
gas // [gas, pairAddress, 0x1c, 0x04, 0x00, 0x00, 0x00]
staticcall
returndatasize 0x00 0x00 returndatacopy //copy token0() results to 0x00 in memory
0x00 mload // Return the token 0 address
}
#define macro HANDLE_REVERT() = takes (0) returns (0) {
// store returndata in memory and reverts accordingly
returndatasize // [returndatasize, success]
0x11
eq
returnRevert
jumpi
completeAction
jump
returnRevert:
REVERT()
completeAction:
}
#define macro SWAP() = {
caller // [msg.sender]
0xc4 calldataload // [tokenInAddress, msg.sender]
eq
doneStep
jumpi
// Start building uniV2Call params from call data
// [pair(0xe4), ReceiverAddress(0x04), tokenInAddress(0xc4), pair(0xe4), amountIn(0xa4)]
0xa4 calldataload // [amountIn]
0xe4 calldataload // [pair, amountIn]
0xc4 calldataload // [tokenInAddress, pair, amountIn]
0x04 calldataload // [senderAddress, tokenInAddress, pair, amountIn]
0xe4 calldataload // [pair, senderAddress, tokenInAddress, pair, amountIn]
UNI_V2_CALL()
doneStep
jump
return 0x00 0x20
doneStep:
}
#define macro FALLBACK() = {
// revert for now
REVERT()
}
#define macro REVERT() = takes (0) returns (0) {
revert
}
#define macro RECEIVE() = takes (1) returns (1) {}
#define macro MAIN() = takes (0) returns (0) {
// This logic handles value deposits and if none,
// Calls the contract function
callvalue // [msg.value]
iszero // [is_msg_value_zero]
iszero // [is_msg_value_non_zero]
deposited // [deposited_jumpdest, is_msg_value_non_zero]
jumpi // []
// Identifies which function to call
msize
msize calldataload // [msg.data[:32],0]
0xe0 shr // [msg.data[:4],0]
// dup1
__FUNC_SIG(swap) eq swap jumpi
FALLBACK()
0x00 0x00 revert
deposited:
}
@ndeto
Copy link
Author

ndeto commented Apr 25, 2025

I extracted this logic from a suite of contracts I wrote in Huff meant to perform swaps as part of an AMM system. This is just provide an example of how your can write smart contracts at assembly level by simply manipulating the stack and calling opcodes. This is for learning purposes only

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment