implement phantom refund after verification
Signed-off-by: Uncle Fatso <uncle.fatso@ghostchain.io>
This commit is contained in:
parent
28cf9d6de8
commit
8d94ab0dd1
@ -21,13 +21,10 @@ contract Gatekeeper is IGatekeeper, Weaver, ReentrancyGuard {
|
||||
using RequestPacking for RequestPacking.RequestPayload;
|
||||
using GovernancePacking for GovernancePacking.GovernancePayload;
|
||||
|
||||
uint256 private constant BOUNTY_DIVISOR = type(uint32).max;
|
||||
uint256 private constant SECP256K1_N = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141;
|
||||
uint256 private constant SECP256K1_Q = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F;
|
||||
uint256 private constant SHIFT_FACTOR = (2**128) % SECP256K1_N;
|
||||
|
||||
uint256 public constant GAS_RECALL_SPENDING = 329188;
|
||||
uint256 public constant EXISTENTIAL_DEPOSIT = 500 * 1e12;
|
||||
uint256 public constant BOUNTY_DIVISOR = type(uint32).max;
|
||||
uint256 public constant MAX_ALLOWED_GAS_PRICE = 3e9; // 3 gwei
|
||||
uint256 public constant EXISTENTIAL_DEPOSIT = 500 * 1e12;
|
||||
uint256 public constant GAS_EXECUTION_BUFFER = 33805;
|
||||
uint256 public constant REGISTRY_INDEX = 0;
|
||||
|
||||
address public override staking;
|
||||
@ -112,21 +109,6 @@ contract Gatekeeper is IGatekeeper, Weaver, ReentrancyGuard {
|
||||
return (publicKey, state.parity, state.session);
|
||||
}
|
||||
|
||||
function verify(
|
||||
bytes calldata call,
|
||||
uint256 rx,
|
||||
uint256 s
|
||||
) external nonReentrant returns (bytes memory) {
|
||||
uint256 px = _extractPublicKey(call);
|
||||
bool validSignature = Verifier.verifyGhost(call, px, rx, s);
|
||||
if (!validSignature) revert BadSignature();
|
||||
|
||||
(bool success, bytes memory data) = address(this).call(call);
|
||||
if (!success) revert ExecutionReverted();
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
function ghost(bytes32 receiver, uint256 amount) external override returns (uint256) {
|
||||
if (msg.sender != staking) revert NotStaking();
|
||||
if (amount < EXISTENTIAL_DEPOSIT) revert NonExistentAmount();
|
||||
@ -137,36 +119,56 @@ contract Gatekeeper is IGatekeeper, Weaver, ReentrancyGuard {
|
||||
return _insertTreeNode(receiver, amount);
|
||||
}
|
||||
|
||||
function verify(
|
||||
bytes calldata call,
|
||||
uint256 rx,
|
||||
uint256 s
|
||||
) external nonReentrant returns (bytes memory) {
|
||||
uint256 gasStart = gasleft();
|
||||
uint256 px = _extractPublicKey(call);
|
||||
|
||||
bool validSignature = Verifier.verifyGhost(call, px, rx, s);
|
||||
if (!validSignature) revert BadSignature();
|
||||
|
||||
(bool success, bytes memory data) = address(this).call(call);
|
||||
if (!success) revert ExecutionReverted();
|
||||
|
||||
uint256 gasPrice = MAX_ALLOWED_GAS_PRICE < tx.gasprice ? MAX_ALLOWED_GAS_PRICE : tx.gasprice;
|
||||
uint256 gasSpent = (gasStart - gasleft() + GAS_EXECUTION_BUFFER) * gasPrice;
|
||||
|
||||
try IStaking(staking).phantomRefund(msg.sender, gasSpent, REGISTRY_INDEX) {}
|
||||
catch {
|
||||
emit VoluntaryVerification(msg.sender, gasSpent);
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
function recall(
|
||||
uint256 exodusSession,
|
||||
uint256 amount,
|
||||
uint256 packed
|
||||
) external override {
|
||||
) external {
|
||||
if (msg.sender != address(this)) revert NotGatekeeper();
|
||||
|
||||
RequestPacking.RequestPayload memory payload = RequestPacking.unpack(packed);
|
||||
if (payload.chainId != block.chainid) revert WrongChainId();
|
||||
|
||||
StorageHistory(storageHistory).trySetTransactionExecuted(exodusSession);
|
||||
|
||||
uint256 bountyAmount = FullMath.mulDiv(amount, BOUNTY_DIVISOR - uint256(payload.bounty), BOUNTY_DIVISOR);
|
||||
uint256 bountyAmount = FullMath.mulDiv(amount, uint256(payload.bounty), BOUNTY_DIVISOR);
|
||||
uint256 receiverAmount = amount - bountyAmount;
|
||||
uint256 bridgeOutImbalance = receiverAmount;
|
||||
|
||||
StorageHistory(storageHistory).trySetTransactionExecuted(exodusSession);
|
||||
IStorageHistory(storageHistory).tryIncreaseBridgeOut(receiverAmount);
|
||||
IStaking(staking).recall(payload.receiver, receiverAmount);
|
||||
|
||||
if (bountyAmount > 0) {
|
||||
uint256 gasSpent = GAS_RECALL_SPENDING * tx.gasprice;
|
||||
(address token, uint256 forGas, uint256 sent) = IStaking(staking).recall(bountyAmount, gasSpent, REGISTRY_INDEX);
|
||||
bridgeOutImbalance += forGas;
|
||||
|
||||
(address token, uint256 sent) = IStaking(staking).recall(bountyAmount, REGISTRY_INDEX);
|
||||
IWETH9(token).withdraw(sent);
|
||||
|
||||
(bool sentSuccess,) = payload.receiver.call{ value: sent }("");
|
||||
if (!sentSuccess) revert SendFailed();
|
||||
}
|
||||
|
||||
IStorageHistory(storageHistory).tryIncreaseBridgeOut(bridgeOutImbalance);
|
||||
|
||||
emit Recalled(payload.receiver, amount);
|
||||
}
|
||||
|
||||
@ -174,7 +176,7 @@ contract Gatekeeper is IGatekeeper, Weaver, ReentrancyGuard {
|
||||
uint256 exodusSession,
|
||||
bytes32 newPublicKey,
|
||||
uint8 newParity
|
||||
) external override {
|
||||
) external {
|
||||
if (msg.sender != address(this)) revert NotGatekeeper();
|
||||
if (newParity % 2 != 0) revert InvalidPublicKey();
|
||||
|
||||
@ -192,7 +194,7 @@ contract Gatekeeper is IGatekeeper, Weaver, ReentrancyGuard {
|
||||
uint256 exodusSession,
|
||||
uint256 packed,
|
||||
bytes calldata
|
||||
) external override returns (bytes memory) {
|
||||
) external returns (bytes memory) {
|
||||
if (msg.sender != address(this)) revert NotGatekeeper();
|
||||
|
||||
GovernancePacking.GovernancePayload memory payload = GovernancePacking.unpack(packed);
|
||||
|
||||
@ -167,6 +167,16 @@ contract GhostStaking is IStaking, GhostAccessControlled {
|
||||
IGatekeeper(gatekeeper).ghost(receiver, amount);
|
||||
}
|
||||
|
||||
function phantomRefund(address receiver, uint256 amount, uint256 registryIndex) external override {
|
||||
if (gatekeeper != msg.sender) revert NotGatekeeper();
|
||||
address treasury = ISTNK(stnk).treasury();
|
||||
|
||||
uint256 gasFtsoAmount = ITreasury(treasury).phantomRefund(amount, registryIndex);
|
||||
uint256 gasGhstAmount = IGHST(ghst).balanceTo(gasFtsoAmount);
|
||||
|
||||
IGHST(ghst).mint(receiver, gasGhstAmount);
|
||||
}
|
||||
|
||||
function recall(address receiver, uint256 amount) external override {
|
||||
if (gatekeeper != msg.sender) revert NotGatekeeper();
|
||||
IGHST(ghst).mint(receiver, amount);
|
||||
@ -174,25 +184,20 @@ contract GhostStaking is IStaking, GhostAccessControlled {
|
||||
|
||||
function recall(
|
||||
uint256 amount,
|
||||
uint256 gasSpent,
|
||||
uint256 registryIndex
|
||||
) external override returns (address reserveToken, uint256 gasGhstAmount, uint256 value) {
|
||||
) external override returns (address reserveToken, uint256 value) {
|
||||
if (gatekeeper != msg.sender) revert NotGatekeeper();
|
||||
|
||||
address treasury = ISTNK(stnk).treasury();
|
||||
uint256 ftsoAmount = IGHST(ghst).balanceFrom(amount);
|
||||
|
||||
uint256 gasFtsoAmount;
|
||||
(reserveToken, value, gasFtsoAmount) = ITreasury(treasury).buyBack(
|
||||
(reserveToken, value) = ITreasury(treasury).buyBack(
|
||||
msg.sender,
|
||||
ftsoAmount,
|
||||
gasSpent,
|
||||
registryIndex
|
||||
);
|
||||
|
||||
gasGhstAmount = IGHST(ghst).balanceTo(gasFtsoAmount);
|
||||
IFTSO(ftso).burn(ftsoAmount - gasFtsoAmount);
|
||||
IGHST(ghst).mint(tx.origin, gasGhstAmount);
|
||||
|
||||
IFTSO(ftso).burn(ftsoAmount);
|
||||
}
|
||||
|
||||
function rebase() public override returns (uint256 bounty) {
|
||||
|
||||
@ -77,23 +77,35 @@ contract GhostTreasury is GhostAccessControlled, ITreasury {
|
||||
emit Withdrawal(token, amount, value);
|
||||
}
|
||||
|
||||
function phantomRefund(
|
||||
uint256 gasSpent,
|
||||
uint256 index
|
||||
) external override returns (uint256 gasValue) {
|
||||
if (!_permissions[STATUS.STAKING][msg.sender]) revert NotApproved();
|
||||
|
||||
address reserveToken = _registry[STATUS.RESERVETOKEN].at(index);
|
||||
if (!_permissions[STATUS.RESERVETOKEN][reserveToken]) revert NotApproved();
|
||||
|
||||
uint256 totalSupply = IERC20(FTSO).totalSupply();
|
||||
gasValue = tokenValue(reserveToken, gasSpent);
|
||||
gasValue = FullMath.mulDiv(gasValue, totalSupply, totalReserves);
|
||||
|
||||
if (gasValue > excessReserves()) revert InsufficientReserves();
|
||||
IFTSO(FTSO).mint(msg.sender, gasValue);
|
||||
}
|
||||
|
||||
function buyBack(
|
||||
address receiver,
|
||||
uint256 amount,
|
||||
uint256 gasSpent,
|
||||
uint256 index
|
||||
) external override returns (address reserveToken, uint256 value, uint256 gasValue) {
|
||||
) external override returns (address reserveToken, uint256 value) {
|
||||
if (!_permissions[STATUS.STAKING][msg.sender]) revert NotApproved();
|
||||
|
||||
reserveToken = _registry[STATUS.RESERVETOKEN].at(index);
|
||||
if (!_permissions[STATUS.RESERVETOKEN][reserveToken]) revert NotApproved();
|
||||
|
||||
uint256 totalSupply = IERC20(FTSO).totalSupply();
|
||||
gasValue = tokenValue(reserveToken, gasSpent);
|
||||
gasValue = FullMath.mulDiv(gasValue, totalSupply, totalReserves);
|
||||
if (amount <= gasValue) revert GasExceedsBounty();
|
||||
|
||||
uint256 reservesToSend = FullMath.mulDiv(amount - gasValue, totalReserves, totalSupply);
|
||||
uint256 reservesToSend = FullMath.mulDiv(amount, totalReserves, totalSupply);
|
||||
totalReserves = totalReserves - reservesToSend;
|
||||
|
||||
value = FullMath.mulDiv(reservesToSend, 1e18, IBondingCalculator(bondCalculator[reserveToken]).fraction());
|
||||
|
||||
@ -17,6 +17,7 @@ interface IGatekeeper {
|
||||
event Ghosted(bytes32 indexed receiver, uint256 indexed amount);
|
||||
event Recalled(address indexed receiver, uint256 indexed amount);
|
||||
event Rotated(bytes32 indexed aggregatedPublicKey, uint8 indexed parity);
|
||||
event VoluntaryVerification(address indexed sender, uint256 indexed gasSpent);
|
||||
|
||||
function staking() external view returns (address);
|
||||
function deployer() external view returns (address);
|
||||
|
||||
@ -69,7 +69,8 @@ interface IStaking {
|
||||
function unwrap(address _to, uint256 _amount) external returns (uint256 sBalance_);
|
||||
function ghost(bytes32 receiver, uint256 amount) external;
|
||||
function recall(address receiver, uint256 amount) external;
|
||||
function recall(uint256 amount, uint256 gasSpent, uint256 registryIndex) external returns (address, uint256, uint256);
|
||||
function recall(uint256 amount, uint256 registryIndex) external returns (address, uint256);
|
||||
function phantomRefund(address receiver, uint256 amount, uint256 registryIndex) external;
|
||||
function rebase() external returns (uint256);
|
||||
|
||||
function index() external view returns (uint256);
|
||||
|
||||
@ -29,7 +29,8 @@ interface ITreasury {
|
||||
uint256 _profit
|
||||
) external returns (uint256);
|
||||
|
||||
function buyBack(address receiver, uint256 amount, uint256 gasSpent, uint256 index) external returns (address, uint256, uint256);
|
||||
function phantomRefund(uint256 gasSpent, uint256 amount) external returns (uint256);
|
||||
function buyBack(address receiver, uint256 amount, uint256 index) external returns (address, uint256);
|
||||
function withdraw(address token, uint256 amount) external;
|
||||
function mint(address _recipient, uint256 _amount) external;
|
||||
function tokenValue(address _token, uint256 _amount) external view returns (uint256 value_);
|
||||
|
||||
@ -22,12 +22,7 @@ library Verifier {
|
||||
) internal view returns (bool) {
|
||||
if (px >= P || rx >= P || s >= N || s == 0) { return false; }
|
||||
if (rx < N) { return verifySpectre(call, px, rx, s); }
|
||||
|
||||
(uint256 py, bool success1) = liftPoint(px);
|
||||
(uint256 ry, bool success2) = liftPoint(rx);
|
||||
if (!success1 || !success2) { return false; }
|
||||
|
||||
return verifyBanshee(call, px, py, rx, ry, s);
|
||||
return verifyBanshee(call, px, rx, s);
|
||||
}
|
||||
|
||||
function verifySpectre(
|
||||
@ -52,14 +47,12 @@ library Verifier {
|
||||
function verifyBanshee(
|
||||
bytes memory call,
|
||||
uint256 px,
|
||||
uint256 py,
|
||||
uint256 rx,
|
||||
uint256 ry,
|
||||
uint256 s
|
||||
) internal pure returns (bool) {
|
||||
// TODO: because I lift it before, do I need to check is it on curve?
|
||||
// both of them Px,Py and Rx,Ry
|
||||
if (!isOnCurve(rx, ry) || !isOnCurve(px, py)) { return false; }
|
||||
) internal view returns (bool) {
|
||||
(uint256 py, bool success1) = liftPoint(px);
|
||||
(uint256 ry, bool success2) = liftPoint(rx);
|
||||
if (!success1 || !success2) { return false; }
|
||||
|
||||
uint256 e = computeChallenge(call, rx, px);
|
||||
if (e == 0) return false; // Cheap and safe
|
||||
@ -96,9 +89,6 @@ library Verifier {
|
||||
}
|
||||
|
||||
function expModPrecompile(uint256 base) internal view returns (uint256 result) {
|
||||
uint256 localEXP = EXP;
|
||||
uint256 localP = P;
|
||||
|
||||
assembly {
|
||||
let pointer := mload(0x40)
|
||||
|
||||
@ -107,8 +97,8 @@ library Verifier {
|
||||
mstore(add(pointer, 0x40), 0x20)
|
||||
|
||||
mstore(add(pointer, 0x60), base)
|
||||
mstore(add(pointer, 0x80), localEXP)
|
||||
mstore(add(pointer, 0xa0), localP)
|
||||
mstore(add(pointer, 0x80), EXP)
|
||||
mstore(add(pointer, 0xa0), P)
|
||||
|
||||
// Modular Exponentiation Precompile (modexp)
|
||||
let success := staticcall(gas(), 0x05, pointer, 0xc0, pointer, 0x20)
|
||||
|
||||
@ -4,7 +4,6 @@ import {Test} from "forge-std/Test.sol";
|
||||
|
||||
import {Gatekeeper} from "../../src/Gatekeeper.sol";
|
||||
import {FullMath} from "../../src/libraries/FullMath.sol";
|
||||
import {Verifier} from "../../src/libraries/Verifier.sol";
|
||||
import {RequestPacking} from "../../src/libraries/Packing.sol";
|
||||
import {IStorageHistory} from "../../src/interfaces/IStorageHistory.sol";
|
||||
import {StorageHistory} from "../../src/types/StorageHistory.sol";
|
||||
@ -142,6 +141,7 @@ contract MockStaking is Test {
|
||||
WETH9 public mockReserve;
|
||||
|
||||
mapping(address => uint256) private _recalledAmounts;
|
||||
mapping(address => uint256) private _phantomedAmounts;
|
||||
|
||||
constructor() {
|
||||
mockReserve = new WETH9();
|
||||
@ -169,9 +169,13 @@ contract MockStaking is Test {
|
||||
_recalledAmounts[receiver] += amount;
|
||||
}
|
||||
|
||||
function recall(uint256 amount, uint256, uint256) external returns (address, uint256, uint256){
|
||||
function recall(uint256 amount, uint256) external returns (address, uint256){
|
||||
_recalledAmounts[tx.origin] += amount;
|
||||
return (address(mockReserve), 0, 0);
|
||||
return (address(mockReserve), 0);
|
||||
}
|
||||
|
||||
function phantomRefund(address who, uint256 amount) external {
|
||||
_phantomedAmounts[who] += amount;
|
||||
}
|
||||
|
||||
function recalledAmount(address who) external view returns (uint256) {
|
||||
@ -387,7 +391,6 @@ contract GatekeeperTest is Test {
|
||||
aliceBalanceBefore = ALICE.balance;
|
||||
bobBalanceBefore = BOB.balance;
|
||||
|
||||
// TODO: revisit
|
||||
// execute bridge out, happened on DKG #2
|
||||
// amount: 210; commission: 50%; BOB
|
||||
// exodus session #12
|
||||
|
||||
@ -14,6 +14,7 @@ contract MockStaking is Test {
|
||||
WETH9 public mockReserve;
|
||||
|
||||
mapping(address => uint256) private _recalledAmounts;
|
||||
mapping(address => uint256) private _phantomedAmounts;
|
||||
|
||||
constructor() {
|
||||
mockReserve = new WETH9();
|
||||
@ -46,25 +47,35 @@ contract MockStaking is Test {
|
||||
_recalledAmounts[receiver] += amount;
|
||||
}
|
||||
|
||||
function recall(uint256 amount, uint256, uint256) external returns (address, uint256, uint256){
|
||||
function recall(uint256 amount, uint256) external returns (address, uint256){
|
||||
_recalledAmounts[tx.origin] += amount;
|
||||
return (address(mockReserve), amount, 0);
|
||||
return (address(mockReserve), amount);
|
||||
}
|
||||
|
||||
function recalledAmount(address who) external view returns (uint256) {
|
||||
function phantomRefund(address who, uint256 amount) external {
|
||||
_phantomedAmounts[who] += amount;
|
||||
}
|
||||
|
||||
function recalledAmounts(address who) external view returns (uint256) {
|
||||
return _recalledAmounts[who];
|
||||
}
|
||||
|
||||
function phantomedAmounts(address who) external view returns (uint256) {
|
||||
return _phantomedAmounts[who];
|
||||
}
|
||||
}
|
||||
|
||||
contract GatekeeperStorageHistoryTest is Test {
|
||||
using RequestPacking for RequestPacking.RequestPayload;
|
||||
|
||||
address constant ALICE = 0x0000000000000000000000000000000000000001;
|
||||
address constant BOB = 0x0000000000000000000000000000000000000002;
|
||||
uint256 constant INIT_AMOUNT = 1337 * 1e18;
|
||||
address constant ALICE = 0x0000000000000000000000000000000000000001;
|
||||
address constant BOB = 0x0000000000000000000000000000000000000002;
|
||||
uint256 constant INIT_AMOUNT = 1337 * 1e18;
|
||||
uint256 private constant BIG_VALUE = 420 * 1e20;
|
||||
|
||||
Gatekeeper gatekeeper;
|
||||
MockStaking staking;
|
||||
WETH9 mockReserve;
|
||||
|
||||
function setUp() public {
|
||||
vm.prank(ALICE);
|
||||
@ -72,6 +83,14 @@ contract GatekeeperStorageHistoryTest is Test {
|
||||
|
||||
staking.runGhost(bytes32(abi.encodePacked(ALICE)), INIT_AMOUNT);
|
||||
gatekeeper = staking.gatekeeper();
|
||||
mockReserve = staking.mockReserve();
|
||||
|
||||
vm.deal(ALICE, BIG_VALUE);
|
||||
|
||||
vm.startPrank(ALICE);
|
||||
mockReserve.deposit{ value: BIG_VALUE }();
|
||||
assertTrue(mockReserve.transfer(address(gatekeeper), BIG_VALUE));
|
||||
vm.stopPrank();
|
||||
}
|
||||
|
||||
function test_correctStorageHistoryInitialization() public view {
|
||||
@ -104,11 +123,10 @@ contract GatekeeperStorageHistoryTest is Test {
|
||||
} else {
|
||||
// forge-lint: disable-next-line(unsafe-typecast)
|
||||
uint256 packed = RequestPacking.pack(0, uint64(block.chainid), BOB);
|
||||
uint256 previousAmount = staking.recalledAmount(BOB);
|
||||
uint256 previousAmount = staking.recalledAmounts(BOB);
|
||||
|
||||
vm.prank(BOB, BOB);
|
||||
staking.runRecall(exodusSession, amountToMaterialize, packed);
|
||||
assertEq(previousAmount + amountToMaterialize, staking.recalledAmount(BOB));
|
||||
assertEq(previousAmount + amountToMaterialize, staking.recalledAmounts(BOB));
|
||||
|
||||
// forge-lint: disable-next-line(unsafe-typecast)
|
||||
amountOut += uint104(amountToMaterialize);
|
||||
@ -123,6 +141,7 @@ contract GatekeeperStorageHistoryTest is Test {
|
||||
function test_inheritanceWorksForHistoricalStorge() public {
|
||||
uint256 exodusSession = 69;
|
||||
uint256 packed = RequestPacking.pack(0, uint64(block.chainid), BOB);
|
||||
|
||||
staking.runRecall(exodusSession, INIT_AMOUNT, packed);
|
||||
staking.runGhost(bytes32(abi.encodePacked(ALICE)), INIT_AMOUNT);
|
||||
|
||||
|
||||
@ -95,8 +95,7 @@ contract GatekeeperRecallTest is Test {
|
||||
function test_recallChainWorks() public {
|
||||
vm.startPrank(INITIALIZER);
|
||||
Gatekeeper gatekeeper = Gatekeeper(payable(staking.gatekeeper()));
|
||||
gatekeeper.updatePublicKeyMetadata(0, 0x36ff5b7f0fc50100b563c6072e499980d6e6aa8528ea0ae6d776cf7e8e96c374, 0);
|
||||
|
||||
gatekeeper.updatePublicKeyMetadata(0, 0xb5cd0d028a5e1b6a4eecb113b7e49c8176385eb77c8f134bc1db4bddd7654de6, 0);
|
||||
vm.stopPrank();
|
||||
|
||||
vm.startPrank(ALICE);
|
||||
@ -108,13 +107,12 @@ contract GatekeeperRecallTest is Test {
|
||||
staking.ghost(bytes32(abi.encodePacked(ALICE)), ghostBalance);
|
||||
vm.stopPrank();
|
||||
|
||||
uint256 bountyPercent = type(uint32).max / 2;
|
||||
uint256 amountToBridge = 42 * 1e15;
|
||||
address evmReceiver = address(0x0000000000000000000000000000000000000002);
|
||||
uint256 actualAmount = 105000000000000000;
|
||||
address actualReceiver = address(0x0000000000000000000000000000000000000002);
|
||||
|
||||
uint256 bobGhstBefore = ghst.balanceOf(BOB);
|
||||
uint256 receiverEthBefore = evmReceiver.balance;
|
||||
uint256 receiverGhstBefore = ghst.balanceOf(evmReceiver);
|
||||
uint256 receiverEthBefore = actualReceiver.balance;
|
||||
uint256 receiverGhstBefore = ghst.balanceOf(actualReceiver);
|
||||
uint256 totalReservesBefore = treasury.totalReserves();
|
||||
uint256 totalSupplyBefore = ftso.totalSupply();
|
||||
uint256 ghostedSupplyBefore = gatekeeper.ghostedSupply();
|
||||
@ -122,78 +120,52 @@ contract GatekeeperRecallTest is Test {
|
||||
vm.txGasPrice(2 gwei);
|
||||
vm.startPrank(BOB, BOB);
|
||||
gatekeeper.verify(
|
||||
hex"bf06188a0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000007a919134c0400000000000000000000000000000000000000000010000000000007a6900000000",
|
||||
0x938640689e6f52929acf2714c5c785e8e725b5df616c449a19bca8a8b300ec8b,
|
||||
0x15b8b8c2934ee96d6a83a24055d83ea4074d251c207584e66d9caf8d653cabf2
|
||||
hex"bf06188a000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000017508f1956a800000000000000000000000000000000000000000020000000000007a6980000000",
|
||||
0x883302783b7f3d253d5bdeb17f39117641acdf061bf3fe457cb505df6b17dfc1,
|
||||
0x8c3fc03b5cb738777ceec396ee8647f782d6670992f8f973de2dfc83379978d5
|
||||
);
|
||||
vm.stopPrank();
|
||||
|
||||
{
|
||||
uint256 bobEarnedGhst = ghst.balanceOf(BOB) - bobGhstBefore;
|
||||
assertTrue(bobEarnedGhst > 0);
|
||||
uint256 bobGhstAfter = ghst.balanceOf(BOB);
|
||||
uint256 receiverGhstAfter = ghst.balanceOf(actualReceiver);
|
||||
|
||||
uint256 bobEarnedFtso = ghst.balanceFrom(bobEarnedGhst);
|
||||
|
||||
uint256 totalReservesAfter = treasury.totalReserves();
|
||||
uint256 totalSupplyAfter = ftso.totalSupply();
|
||||
|
||||
uint256 gasUsed = 2 * 1e9 * 329188;
|
||||
uint256 gasReserveValue = treasury.tokenValue(address(reserve), gasUsed);
|
||||
uint256 expectedGasFtso = gasReserveValue * totalSupplyAfter / totalReservesAfter;
|
||||
|
||||
uint256 simulatedGhst = ghst.balanceTo(expectedGasFtso);
|
||||
uint256 dynamicGasPriceEstimation = ghst.balanceFrom(simulatedGhst);
|
||||
|
||||
assertApproxEqAbs(bobEarnedFtso, dynamicGasPriceEstimation, 1);
|
||||
assertTrue(
|
||||
ghst.balanceFrom(bobGhstAfter - bobGhstBefore) * totalReservesBefore / totalSupplyBefore
|
||||
> gatekeeper.GAS_EXECUTION_BUFFER()
|
||||
);
|
||||
assertTrue(bobGhstAfter > bobGhstBefore);
|
||||
assertApproxEqAbs(receiverGhstAfter - receiverGhstBefore, actualAmount / 2, 1e18);
|
||||
}
|
||||
|
||||
{
|
||||
uint256 expectedBounty = amountToBridge * bountyPercent / type(uint32).max;
|
||||
uint256 expectedMintToReceiver = amountToBridge - expectedBounty;
|
||||
uint256 actualMintToReceiver = ghst.balanceOf(evmReceiver) - receiverGhstBefore;
|
||||
assertApproxEqAbs(actualMintToReceiver, expectedMintToReceiver, 1);
|
||||
uint256 ftsoAmount = ghst.balanceFrom(actualAmount / 2);
|
||||
uint256 expectedReservesToSend = ftsoAmount * totalReservesBefore / totalSupplyBefore;
|
||||
uint256 expectedEthRefund = expectedReservesToSend * 1e18 / calculator.fraction();
|
||||
|
||||
expectedEthRefund = expectedEthRefund * 1e18 / 1e9;
|
||||
assertApproxEqAbs(actualReceiver.balance, receiverEthBefore + expectedEthRefund, 1);
|
||||
}
|
||||
|
||||
{
|
||||
uint256 receiverEthAfter = evmReceiver.balance;
|
||||
uint256 receivedEth = receiverEthAfter - receiverEthBefore;
|
||||
assertTrue(receivedEth > 0);
|
||||
|
||||
uint256 totalReservesAfter = treasury.totalReserves();
|
||||
uint256 totalSupplyAfter = ftso.totalSupply();
|
||||
|
||||
uint256 expectedBounty = amountToBridge * bountyPercent / type(uint32).max;
|
||||
uint256 totalBountyFtso = ghst.balanceFrom(expectedBounty);
|
||||
uint256 totalBountyReserveValue = totalBountyFtso * totalReservesAfter / totalSupplyAfter;
|
||||
|
||||
uint256 fraction = calculator.fraction();
|
||||
uint256 totalBountyInEth = totalBountyReserveValue * 1e18 / fraction;
|
||||
totalBountyInEth = totalBountyInEth * 1e18 / 1e9;
|
||||
|
||||
uint256 physicalGasSpentEth = 2 * 1e9 * 329188; // 2 gwei * gas Used
|
||||
assertApproxEqAbs(receivedEth, totalBountyInEth - physicalGasSpentEth, 1);
|
||||
}
|
||||
|
||||
{
|
||||
uint256 totalReservesAfter = treasury.totalReserves();
|
||||
uint256 totalSupplyAfter = ftso.totalSupply();
|
||||
|
||||
actualAmount = treasury.tokenValue(address(reserve), actualReceiver.balance);
|
||||
uint256 backingRatioBefore = totalReservesBefore * 1e18 / totalSupplyBefore;
|
||||
uint256 backingRatioAfter = totalReservesAfter * 1e18 / totalSupplyAfter;
|
||||
uint256 receivedValue = treasury.tokenValue(address(reserve), evmReceiver.balance);
|
||||
uint256 backingRatioAfter = treasury.totalReserves() * 1e18 / ftso.totalSupply();
|
||||
|
||||
assertEq(backingRatioAfter, backingRatioBefore);
|
||||
assertApproxEqAbs(totalReservesAfter + receivedValue, totalReservesBefore, 2000); // 2e-16%
|
||||
uint256 gasFtsoMinted = ghst.balanceFrom(ghst.balanceOf(BOB) - bobGhstBefore);
|
||||
uint256 expectedDelta = (backingRatioBefore * gasFtsoMinted) / totalSupplyBefore;
|
||||
|
||||
assertApproxEqAbs(backingRatioAfter, backingRatioBefore - expectedDelta, 1000);
|
||||
|
||||
vm.prank(GOVERNOR);
|
||||
treasury.auditReserves();
|
||||
assertEq(treasury.totalReserves() + receivedValue, totalReservesBefore);
|
||||
assertApproxEqAbs(treasury.totalReserves() * 1e18 / ftso.totalSupply(), backingRatioBefore - expectedDelta, 2000);
|
||||
}
|
||||
|
||||
{
|
||||
uint256 expectedMintToReceiver = amountToBridge * (type(uint32).max - bountyPercent) / type(uint32).max;
|
||||
uint256 bobGhstAfter = ghst.balanceOf(BOB);
|
||||
assertApproxEqAbs(ghostedSupplyBefore - bobGhstAfter - expectedMintToReceiver, gatekeeper.ghostedSupply(), 1);
|
||||
uint256 receiverGhstAfter = ghst.balanceOf(actualReceiver);
|
||||
uint256 receiverImbalance = receiverGhstAfter - receiverGhstBefore;
|
||||
assertEq(ghostedSupplyBefore - receiverImbalance, staking.ghostedSupply());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user