Skip to content

Commit c6f692f

Browse files
authored
Merge pull request #10 from primitivefinance/upgrade/enigma
Upgrade/enigma
2 parents d46a1a3 + 6eb8265 commit c6f692f

File tree

15 files changed

+195
-152
lines changed

15 files changed

+195
-152
lines changed

contracts/Compiler.sol renamed to contracts/Decompiler.sol

Lines changed: 21 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -7,18 +7,18 @@ import "@rari-capital/solmate/src/tokens/ERC20.sol";
77
import "./HyperLiquidity.sol";
88
import "./HyperSwap.sol";
99

10-
/// @title Enigma Compiler
10+
/// @title Enigma Decompiler
1111
/// @notice Main contract of the Enigma that implements instruction processing.
1212
/// @dev Eliminates the use of function signatures. Expects encoded bytes as msg.data in the fallback.
13-
contract Compiler is HyperLiquidity, HyperSwap {
13+
contract Decompiler is HyperLiquidity, HyperSwap {
1414
// --- Fallback --- //
1515

1616
/// @notice Main touchpoint for receiving calls.
1717
/// @dev Critical: data must be encoded properly to be processed.
1818
/// @custom:security Critical. Guarded against re-entrancy. This is like the bank vault door.
1919
/// @custom:mev Higher level security checks must be implemented by calling contract.
2020
fallback() external payable lock {
21-
if (msg.data[0] != INSTRUCTION_JUMP) _process(msg.data);
21+
if (msg.data[0] != Instructions.INSTRUCTION_JUMP) _process(msg.data);
2222
else _jumpProcess(msg.data);
2323
_settleBalances();
2424
}
@@ -30,10 +30,13 @@ contract Compiler is HyperLiquidity, HyperSwap {
3030
/// @custom:security High. Without pairIds to loop through, no token amounts are settled.
3131
uint16[] internal _tempPairIds;
3232

33+
/// @dev Token -> Touched Flag. Stored temporary to signal which token reserves were tapped.
34+
mapping(address => bool) internal _addressCache;
35+
3336
/// @dev Flag set to `true` during `_process`. Set to `false` during `_settleToken`.
3437
/// @custom:security High. Referenced in settlement to pay for tokens due.
3538
function _cacheAddress(address token, bool flag) internal {
36-
addressCache[token] = flag;
39+
_addressCache[token] = flag;
3740
}
3841

3942
// --- Internal --- //
@@ -61,66 +64,40 @@ contract Compiler is HyperLiquidity, HyperSwap {
6164
emit Debit(token, amount);
6265
}
6366

64-
/// @notice First byte should always be the INSTRUCTION_JUMP Enigma code.
65-
/// @dev Expects a special encoding method for multiple instructions.
66-
/// @param data Includes opcode as byte at index 0. First byte should point to next instruction.
67-
/// @custom:security Critical. Processes multiple instructions. Data must be encoded perfectly.
68-
function _jumpProcess(bytes calldata data) internal {
69-
uint8 length = uint8(data[1]);
70-
uint8 pointer = JUMP_PROCESS_START_POINTER; // note: [opcode, length, pointer, ...instruction, pointer, ...etc]
71-
uint256 start;
72-
73-
// For each instruction set...
74-
for (uint256 i; i != length; ++i) {
75-
// Start at the index of the first byte of the next instruction.
76-
start = pointer;
77-
78-
// Set the new pointer to the next instruction, located at the pointer.
79-
pointer = uint8(data[pointer]);
80-
81-
// The `start:` includes the pointer byte, while the `:end` `pointer` is excluded.
82-
if (pointer > data.length) revert JumpError(pointer);
83-
bytes calldata instruction = data[start:pointer];
84-
85-
// Process the instruction.
86-
_process(instruction[1:]); // note: Removes the pointer to the next instruction.
87-
}
88-
}
89-
9067
/// @notice Single instruction processor that will forward instruction to appropriate function.
9168
/// @dev Critical: Every token of every pair interacted with is cached to be settled later.
9269
/// @param data Encoded Enigma data. First byte must be an Enigma instruction.
9370
/// @custom:security Critical. Directly sends instructions to be executed.
94-
function _process(bytes calldata data) internal returns (uint16 pairId) {
71+
function _process(bytes calldata data) internal override {
9572
uint48 poolId;
9673
bytes1 instruction = bytes1(data[0] & 0x0f);
97-
if (instruction == UNKNOWN) revert UnknownInstruction();
74+
if (instruction == Instructions.UNKNOWN) revert UnknownInstruction();
9875

99-
if (instruction == ADD_LIQUIDITY) {
76+
if (instruction == Instructions.ADD_LIQUIDITY) {
10077
(poolId, ) = _addLiquidity(data);
101-
} else if (instruction == REMOVE_LIQUIDITY) {
78+
} else if (instruction == Instructions.REMOVE_LIQUIDITY) {
10279
(poolId, , ) = _removeLiquidity(data);
103-
} else if (instruction == SWAP) {
80+
} else if (instruction == Instructions.SWAP) {
10481
(poolId, ) = _swapExactForExact(data);
105-
} else if (instruction == CREATE_POOL) {
82+
} else if (instruction == Instructions.CREATE_POOL) {
10683
(poolId, , ) = _createPool(data);
107-
} else if (instruction == CREATE_CURVE) {
84+
} else if (instruction == Instructions.CREATE_CURVE) {
10885
_createCurve(data);
109-
} else if (instruction == CREATE_PAIR) {
86+
} else if (instruction == Instructions.CREATE_PAIR) {
11087
_createPair(data);
11188
} else {
11289
revert UnknownInstruction();
11390
}
11491

11592
// note: Only pool interactions have a non-zero poolId.
11693
if (poolId != 0) {
117-
pairId = uint16(poolId >> 32);
94+
uint16 pairId = uint16(poolId >> 32);
11895
// Add the pair to the array to track all the pairs that have been interacted with.
11996
_tempPairIds.push(pairId); // note: critical to push the tokens interacted with.
12097
// Caching the addresses to settle the pools interacted with in the fallback function.
12198
Pair memory pair = pairs[pairId]; // note: pairIds start at 1 because nonce is incremented first.
122-
if (!addressCache[pair.tokenBase]) _cacheAddress(pair.tokenBase, true);
123-
if (!addressCache[pair.tokenQuote]) _cacheAddress(pair.tokenQuote, true);
99+
if (!_addressCache[pair.tokenBase]) _cacheAddress(pair.tokenBase, true);
100+
if (!_addressCache[pair.tokenQuote]) _cacheAddress(pair.tokenQuote, true);
124101
}
125102
}
126103

@@ -144,7 +121,7 @@ contract Compiler is HyperLiquidity, HyperSwap {
144121
/// @param token Target token to pay or credit.
145122
/// @custom:security Critical. Handles crediting accounts or requesting payment for debits.
146123
function _settleToken(address token) internal {
147-
if (!addressCache[token]) return; // note: Early short circuit, since attempting to settle twice is common for big orders.
124+
if (!_addressCache[token]) return; // note: Early short circuit, since attempting to settle twice is common for big orders.
148125

149126
uint256 global = globalReserves[token];
150127
uint256 actual = _balanceOf(token, address(this));
@@ -178,4 +155,6 @@ contract Compiler is HyperLiquidity, HyperSwap {
178155
_applyCredit(token, amount);
179156
SafeTransferLib.safeTransferFrom(ERC20(token), msg.sender, address(this), amount);
180157
}
158+
159+
// --- View --- //
181160
}

contracts/EnigmaVirtualMachine.sol

Lines changed: 98 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,13 @@ import "./interfaces/IEnigma.sol";
55
import "./interfaces/IERC20.sol";
66
import "./libraries/Decoder.sol";
77
import "./libraries/Instructions.sol";
8+
import "./libraries/SafeCast.sol";
89

9-
/// @title Enigma Virtual Machine
10-
/// @notice Defines the possible instruction set which must be processed in a higher-level compiler.
11-
/// @dev Implements low-level `balanceOf`, re-entrancy guard, instruction constants and state.
10+
/// @title Enigma Virtual Machine.
11+
/// @notice Stores the state of the Enigma with functions to change state.
12+
/// @dev Implements low-level internal virtual functions, re-entrancy guard and state.
1213
abstract contract EnigmaVirtualMachine is IEnigma {
14+
using SafeCast for uint256;
1315
// --- Reentrancy --- //
1416
modifier lock() {
1517
if (locked != 1) revert LockedError();
@@ -19,7 +21,51 @@ abstract contract EnigmaVirtualMachine is IEnigma {
1921
locked = 1;
2022
}
2123

24+
// --- View --- //
25+
26+
/// @inheritdoc IEnigmaView
27+
function checkJitLiquidity(address account, uint48 poolId)
28+
public
29+
view
30+
virtual
31+
returns (uint256 distance, uint256 timestamp)
32+
{
33+
uint256 previous = positions[account][poolId].blockTimestamp;
34+
timestamp = _blockTimestamp();
35+
distance = timestamp - previous;
36+
}
37+
2238
// --- Internal --- //
39+
/// @dev Must be implemented by the highest level contract.
40+
/// @notice Processing logic for instructions.
41+
function _process(bytes calldata data) internal virtual;
42+
43+
/// @notice First byte should always be the INSTRUCTION_JUMP Enigma code.
44+
/// @dev Expects a special encoding method for multiple instructions.
45+
/// @param data Includes opcode as byte at index 0. First byte should point to next instruction.
46+
/// @custom:security Critical. Processes multiple instructions. Data must be encoded perfectly.
47+
function _jumpProcess(bytes calldata data) internal {
48+
uint8 length = uint8(data[1]);
49+
uint8 pointer = JUMP_PROCESS_START_POINTER; // note: [opcode, length, pointer, ...instruction, pointer, ...etc]
50+
uint256 start;
51+
52+
// For each instruction set...
53+
for (uint256 i; i != length; ++i) {
54+
// Start at the index of the first byte of the next instruction.
55+
start = pointer;
56+
57+
// Set the new pointer to the next instruction, located at the pointer.
58+
pointer = uint8(data[pointer]);
59+
60+
// The `start:` includes the pointer byte, while the `:end` `pointer` is excluded.
61+
if (pointer > data.length) revert JumpError(pointer);
62+
bytes calldata instruction = data[start:pointer];
63+
64+
// Process the instruction.
65+
_process(instruction[1:]); // note: Removes the pointer to the next instruction.
66+
}
67+
}
68+
2369
/// @dev Gas optimized `balanceOf` method.
2470
function _balanceOf(address token, address account) internal view returns (uint256) {
2571
(bool success, bytes memory data) = token.staticcall(
@@ -39,24 +85,62 @@ abstract contract EnigmaVirtualMachine is IEnigma {
3985
return JUST_IN_TIME_LIQUIDITY_POLICY;
4086
}
4187

42-
// --- Instructions --- //
43-
bytes1 public constant UNKNOWN = 0x00;
44-
bytes1 public constant ADD_LIQUIDITY = 0x01;
45-
bytes1 public constant REMOVE_LIQUIDITY = 0x03;
46-
bytes1 public constant SWAP = 0x05;
47-
bytes1 public constant CREATE_POOL = 0x0B;
48-
bytes1 public constant CREATE_PAIR = 0x0C;
49-
bytes1 public constant CREATE_CURVE = 0x0D;
50-
bytes1 public constant INSTRUCTION_JUMP = 0xAA;
88+
// --- Global --- //
89+
90+
/// @dev Most important function because it manages the solvency of the Engima.
91+
/// @custom:security Critical. Global balances of tokens are compared with the actual `balanceOf`.
92+
function _increaseGlobal(address token, uint256 amount) internal {
93+
globalReserves[token] += amount;
94+
emit IncreaseGlobal(token, amount);
95+
}
96+
97+
/// @dev Equally important to `_increaseGlobal`.
98+
/// @custom:security Critical. Same as above. Implicitly reverts on underflow.
99+
function _decreaseGlobal(address token, uint256 amount) internal {
100+
globalReserves[token] -= amount;
101+
emit DecreaseGlobal(token, amount);
102+
}
103+
104+
// --- Positions --- //
105+
106+
/// @dev Assumes the position is properly allocated to an account by the end of the transaction.
107+
/// @custom:security High. Only method of increasing the liquidity held by accounts.
108+
function _increasePosition(uint48 poolId, uint256 deltaLiquidity) internal {
109+
Position storage pos = positions[msg.sender][poolId];
110+
111+
pos.liquidity += deltaLiquidity.toUint128();
112+
pos.blockTimestamp = _blockTimestamp();
113+
114+
emit IncreasePosition(msg.sender, poolId, deltaLiquidity);
115+
}
116+
117+
/// @dev Equally important as `_increasePosition`.
118+
/// @custom:security Critical. Includes the JIT liquidity check. Implicitly reverts on liquidity underflow.
119+
function _decreasePosition(uint48 poolId, uint256 deltaLiquidity) internal {
120+
Position storage pos = positions[msg.sender][poolId];
121+
122+
pos.liquidity -= deltaLiquidity.toUint128();
123+
pos.blockTimestamp = _blockTimestamp();
124+
125+
emit DecreasePosition(msg.sender, poolId, deltaLiquidity);
126+
}
127+
128+
/// @dev Reverts if liquidity was allocated within time elapsed in seconds returned by `_liquidityPolicy`.
129+
/// @custom:security High. Must be used in place of `_decreasePosition` in most scenarios.
130+
function _decreasePositionCheckJit(uint48 poolId, uint256 deltaLiquidity) internal {
131+
(uint256 distance, uint256 timestamp) = checkJitLiquidity(msg.sender, poolId);
132+
if (_liquidityPolicy() > distance) revert JitLiquidity(distance);
133+
134+
_decreasePosition(poolId, deltaLiquidity);
135+
}
136+
51137
// --- State --- //
52138
/// @dev Pool id -> Pair of a Pool.
53139
mapping(uint16 => Pair) public pairs;
54140
/// @dev Pool id -> Pool Data Structure.
55141
mapping(uint48 => Pool) public pools;
56142
/// @dev Pool id -> Curve Data Structure stores parameters.
57143
mapping(uint32 => Curve) public curves;
58-
/// @dev Token -> Touched Flag. Stored temporary to signal which token reserves were tapped.
59-
mapping(address => bool) public addressCache;
60144
/// @dev Raw curve parameters packed into bytes32 mapped onto a Curve id when it was deployed.
61145
mapping(bytes32 => uint32) public getCurveIds;
62146
/// @dev Token -> Physical Reserves.

contracts/HyperLiquidity.sol

Lines changed: 1 addition & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
pragma solidity 0.8.10;
33

44
import "@primitivefi/rmm-core/contracts/libraries/ReplicationMath.sol";
5-
import "@primitivefi/rmm-core/contracts/libraries/SafeCast.sol";
65

76
import "./interfaces/IERC20.sol";
87
import "./EnigmaVirtualMachine.sol";
@@ -15,17 +14,6 @@ abstract contract HyperLiquidity is EnigmaVirtualMachine {
1514

1615
// --- View --- //
1716

18-
/// @inheritdoc IEnigmaView
19-
function checkJitLiquidity(address account, uint48 poolId)
20-
public
21-
view
22-
returns (uint256 distance, uint256 timestamp)
23-
{
24-
Position memory pos = positions[account][poolId];
25-
timestamp = _blockTimestamp();
26-
distance = timestamp - pos.blockTimestamp;
27-
}
28-
2917
/// @inheritdoc IEnigmaView
3018
function getLiquidityMinted(
3119
uint48 poolId,
@@ -40,47 +28,6 @@ abstract contract HyperLiquidity is EnigmaVirtualMachine {
4028

4129
// --- Internal --- //
4230

43-
// --- Global --- //
44-
45-
/// @dev Most important function because it manages the solvency of the Engima.
46-
/// @custom:security Critical. Global balances of tokens are compared with the actual `balanceOf`.
47-
function _increaseGlobal(address token, uint256 amount) internal {
48-
globalReserves[token] += amount;
49-
emit IncreaseGlobal(token, amount);
50-
}
51-
52-
/// @dev Equally important to `_increaseGlobal`.
53-
/// @custom:security Critical. Same as above. Implicitly reverts on underflow.
54-
function _decreaseGlobal(address token, uint256 amount) internal {
55-
globalReserves[token] -= amount;
56-
emit DecreaseGlobal(token, amount);
57-
}
58-
59-
// --- Posiitons --- //
60-
61-
/// @dev Assumes the position is properly allocated to an account by the end of the transaction.
62-
/// @custom:security High. Only method of increasing the liquidity held by accounts.
63-
function _increasePosition(uint48 poolId, uint256 deltaLiquidity) internal {
64-
Position storage pos = positions[msg.sender][poolId];
65-
pos.liquidity += deltaLiquidity.toUint128();
66-
pos.blockTimestamp = _blockTimestamp();
67-
68-
emit IncreasePosition(msg.sender, poolId, deltaLiquidity);
69-
}
70-
71-
/// @dev Equally important as `_decreasePosition`.
72-
/// @custom:security Critical. Includes the JIT liquidity check. Implicitly reverts on liquidity underflow.
73-
function _decreasePosition(uint48 poolId, uint256 deltaLiquidity) internal {
74-
Position storage pos = positions[msg.sender][poolId];
75-
(uint256 distance, uint256 timestamp) = checkJitLiquidity(msg.sender, poolId);
76-
if (_liquidityPolicy() > distance) revert JitLiquidity(pos.blockTimestamp, timestamp);
77-
78-
pos.liquidity -= deltaLiquidity.toUint128();
79-
pos.blockTimestamp = timestamp.toUint128();
80-
81-
emit DecreasePosition(msg.sender, poolId, deltaLiquidity);
82-
}
83-
8431
// --- Liquidity --- //
8532

8633
/// @notice Increases internal reserves, liquidity position, and global token balances.
@@ -163,7 +110,7 @@ abstract contract HyperLiquidity is EnigmaVirtualMachine {
163110
pool.internalLiquidity -= deltaLiquidity;
164111
pool.blockTimestamp = _blockTimestamp();
165112

166-
_decreasePosition(poolId, deltaLiquidity);
113+
_decreasePositionCheckJit(poolId, deltaLiquidity);
167114

168115
Pair memory pair = pairs[pairId];
169116
_decreaseGlobal(pair.tokenBase, deltaBase);

contracts/HyperSwap.sol

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
pragma solidity 0.8.10;
33

44
import "@primitivefi/rmm-core/contracts/libraries/ReplicationMath.sol";
5-
import "@primitivefi/rmm-core/contracts/libraries/SafeCast.sol";
65

76
import "./EnigmaVirtualMachine.sol";
87

contracts/interfaces/enigma/IEnigmaErrors.sol

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ pragma solidity 0.8.10;
44
/// @title IEnigmaErrors
55
/// @dev All errors thrown by the Enigma and its higher level contracts.
66
interface IEnigmaErrors {
7-
// --- Compiler --- //
7+
// --- Decompiler --- //
88
/// @dev Thrown when attempting to remove more internal token balance than owned by `msg.sender`.
99
error DrawBalance();
1010
/// @dev Thrown when the jump pointer is further than the length of the next instruction.
@@ -50,7 +50,7 @@ interface IEnigmaErrors {
5050

5151
// --- Special --- //
5252
/// @dev Thrown if the JIT liquidity condition is false.
53-
error JitLiquidity(uint256 lastTime, uint256 timestamp);
53+
error JitLiquidity(uint256 distance);
5454

5555
// --- Swap --- //
5656
/// @dev Thrown if the effects of a swap put the pool in an invalid state according the the trading function.

contracts/interfaces/enigma/IEnigmaEvents.sol

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ interface IEnigmaEvents {
1818
/// @custom:security High.
1919
event DecreaseGlobal(address indexed token, uint256 amount);
2020

21-
// --- Compiler --- //
21+
// --- Decompiler --- //
2222
/// @dev A payment requested by this contract that must be paid by the `msg.sender` account.
2323
event Debit(address indexed token, uint256 amount);
2424
/// @dev A payment that is paid out to the `msg.sender` account from this contract.

0 commit comments

Comments
 (0)