Transaction Hash:
Block:
21159468 at Nov-10-2024 07:49:35 PM +UTC
Transaction Fee:
0.0035064233690689 ETH
$8.89
Gas Used:
65,315 Gas / 53.68481006 Gwei
Emitted Events:
334 |
Kiru.Transfer( from=[Receiver] Kiru, to=[Sender] 0x13317023bf6fe079ec1b4f2200e7b0a131be74d4, value=428333225182185299976328414 )
|
335 |
Kiru.Buy( chad=[Sender] 0x13317023bf6fe079ec1b4f2200e7b0a131be74d4, amountIn=160000000000000000, amountOut=428333225182185299976328414, r0=27341518697507652155, r1=72767099051871769336687464604 )
|
Account State Difference:
Address | Before | After | State Difference | ||
---|---|---|---|---|---|
0x13317023...131bE74D4 |
1.095531465167968446 Eth
Nonce: 4639
|
0.932025041798899546 Eth
Nonce: 4640
| 0.1635064233690689 | ||
0x95222290...5CC4BAfe5
Miner
| (beaverbuild) | 19.133473822669565752 Eth | 19.134780122669565752 Eth | 0.0013063 | |
0xe04d4E49...5A4C01da2 | 27.721364799780838046 Eth | 27.881364799780838046 Eth | 0.16 |
Execution Trace
ETH 0.16
Kiru.deposit( out=403049248937605738639831543 )
deposit[Kiru (ln:229)]
// SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.26; /* * Welcome to Kiru Land. * *//** * @title Kiru 👼🏻 * * @author Kiru * * @notice Kiru is an efficient contract for the Kiru tokenized bonding curve. * Unification of the asset and it's canonical market under a single * contract allows for precise control over the market-making strategy, * fully automated. * * :::: ° :::: * ::: . ------------ . ::: * :: ° |... ° | - - - - ◓ :: * : o | ... . | : * :: . | ° ... | :: * ::: ◓ - - - - | . ...| ::: * :::: ------------ :::: * * @dev Jita market, the bonding-curve of the Kiru token, executes buys and * sells with different dynamics to optimise for price volatility on buys * and deep liquidity on sells. * Driven by the skew factor α(t), Jita binds price appreciation to * uniform volume. */ contract Kiru { ////////////////////////////////////////////////////////////// /////////////////////////// State //////////////////////////// ////////////////////////////////////////////////////////////// /* * -===: * :-*%%%#**#@@@@@@%#-. * .+#@@@@@@@@@@@%#%@@@@@@@%*: * +%@@@@@@@@@@@%%@@%#%@@@@@@%%%#: * +%@@@@@@@@@%%%@@@@@%###@@%##%%@@@# * =#@@@%%%@@@@@@@@@@@@%####%###**%@@@@@* * -%@@@%@@@@@@@@@@@@@%##*##*##****%@@@@@@@+ * -#@%%@@@%@@@@@@@@@%######**#**###%%@@@@@@@+ * .#@%%@%##@@@@@@@@%#**#%%%#****##**#%@@@@@@@@= * +%%#%%#%@@@@@@@%%#***#@@@@%#*###**#%@@@@@@@@@- * .*#*##%@@@@@@@%##**#%%@@@@@@@%@@@%#*#%%@@@@%%@@- * .+***%@@@@@@@%#*+=. :#@@@@@@+. .=+*#%@@@@@#%@@- * =#%#*: *#*#%@@@@@@%#**+: :*%%%@@@@@%.=*=-:+#%%%@@@%*#%= * =@@@@@@@@@%**####@@@@@%#*+*+ :=+@@@@@@@% :%# -*#%%@@@%==#- * -%@@@@@@@@@@@@##%##@@@@%#****. . -@@@@@@@@. .@@*=+#%%%@@%--= * :%@@@@@@@@@@%@%#%%%##%@@@%*+*##-#*-..:*@@@@@@@@=:+@@%++*#%%@@%::. * +@@@@@@@@@@%=:=#%%###*#%@%#*==*##%-. .+@@@@@@%%:-#@%=+#*#%%@@- * +@@@@@@@@@@@= :*##**#***##***+=+*#*+==*@@@@%%%@@@@@#+=*###%%@* * +@@@@@@@@@@@+ :*#*+*#*****##***+==*#%@@@@@#+*@@@%*+=+*%%##%%@#. * =@@@@@@@@@@@#. :**-.+#*****#**##+=---==+#%%%%%#*===++*#%####%@@@: * =@@@@@@@@@@@@* =*: **=-+*+*+++*##++**+==+*+=-=+*#**###=+*##%@@%: * -@@@@@@@@@@@@# -. -: :*@@@@#++*%@@@%##+=++=*#%+ -*#+*@@@= * :%@@@@@@@@@@@@# :+%@@@@%#++%@@*+##+-..+%* -*#..*@@@: * :%@@@@@@@@@@@@@# -+#%@@@@%*++#*+#%%+:.:*= -#*+ :%@@: * :%@@@@@@@@@@@@@@# .=##++#@@@@%*+=+%@@%+: -*+ :%@#. * .%@@%%@@@%@@@@@@@# =#%%%*++%@@@@%*+*%@@%=. :: :%#: * .%@-.=@@@%@@@@@@@%: -*%%@@@%*+*%@@@@%+=%@@%= :#+ * .+. *@@##@@@%@@@* :+#%@@@@@@%*+*%@@@%++%@@%= :+: * .#@*:*@@%@@@*. -*%@@@@@#*#%#*=+##*#++#%%#**+---- * .*+.=@@%+%@*. -*%@@@@@*=*@@*=+---=*+%@@@%#%@@@@@#. * =%*.-%%= .-++=+#@@@@@%++%@%+=. -#@@@@*%@@##@@@*. * .: .** -*%%#+=#@@@@@@#+#@@@*. @@@@#*@@%*##+@@@%: * =##=---+%@@@@@@*#@@@*. @@@@#*@@%*##+@@@@- * =**--**+#@@@@@%+*@@@*. @@@@#*@@%*##+@@@@- * =##+-*##*#@@@%*=*%%*: @@@@**@@%*%%+@@@@- * .====-*#%%%%#+=*##+: .=****#@@%%%%@@%= * :=+*+:..... .=*###%@@@@@@@= * .+#*+*%%%%: * *//** * @notice The Magical Token. */ string public constant name = "Kiru"; string public constant symbol = "KIRU"; uint8 public constant decimals = 18; mapping(address => uint) private _balanceOf; // slot 0 mapping(address => mapping(address => uint)) private _allowance; // slot 1 uint cumulatedFees; // slot 2 uint256 _totalSupply; // slot 3 /** * @notice The initial skew factor α(0) scaled by 1e6 */ uint immutable ALPHA_INIT; /** * @notice The slash factor ϕ scaled by 1e6 */ uint immutable PHI_FACTOR; /** * @notice The initial reserve of KIRU R₁(0) */ uint immutable R1_INIT; /** * @notice Buyback address */ address public immutable TREASURY; /** * @notice constant storage slots */ bytes32 immutable SELF_BALANCE_SLOT; bytes32 immutable TREASURY_BALANCE_SLOT; bytes32 immutable BURN_BALANCE_SLOT; /** * @notice event signatures */ bytes32 constant TRANSFER_EVENT_SIG = keccak256("Transfer(address,address,uint256)"); bytes32 constant APPROVAL_EVENT_SIG = keccak256("Approval(address,address,uint256)"); bytes32 constant BUY_EVENT_SIG = keccak256("Buy(address,uint256,uint256,uint256,uint256)"); bytes32 constant SELL_EVENT_SIG = keccak256("Sell(address,uint256,uint256,uint256,uint256)"); event Transfer(address indexed from, address indexed to, uint value); event Approval(address indexed owner, address indexed spender, uint value); event Buy(address indexed chad, uint amountIn, uint amountOut, uint r0, uint r1); event Sell(address indexed jeet, uint amountIn, uint amountOut, uint r0, uint r1); ////////////////////////////////////////////////////////////// //////////////////////// Constructor ///////////////////////// ////////////////////////////////////////////////////////////// /** * @notice t=0 state */ constructor( uint _alpha, uint _phi, uint _r1Init, uint _supply, address _treasury ) payable { ALPHA_INIT = _alpha; PHI_FACTOR = _phi; R1_INIT = _r1Init / 2; TREASURY = _treasury; _totalSupply = _supply; bytes32 _SELF_BALANCE_SLOT; bytes32 _TREASURY_BALANCE_SLOT; assembly { let ptr := mload(0x40) /* * Load balanceOf[address(this)] slot */ mstore(ptr, address()) mstore(add(ptr, 0x20), 0) _SELF_BALANCE_SLOT := keccak256(ptr, 0x40) /* * Load balanceOf[_treasury] slot */ mstore(ptr, _treasury) _TREASURY_BALANCE_SLOT := keccak256(ptr, 0x40) /* * Set the initial balances */ sstore(_SELF_BALANCE_SLOT, _r1Init) sstore(_TREASURY_BALANCE_SLOT, sub(_supply, _r1Init)) } /* * set constants */ SELF_BALANCE_SLOT = _SELF_BALANCE_SLOT; TREASURY_BALANCE_SLOT = _TREASURY_BALANCE_SLOT; } ////////////////////////////////////////////////////////////// //////////////////////////// Buy ///////////////////////////// ////////////////////////////////////////////////////////////// /** * Nomenclature: * * R₀ = ETH reserves * R₁ = Kiru reserves * x = input amount * y = output amount * K = invariant R₀ * R₁ * α = skew factor * var(0) = value of var at t=0 * var(t) = value of var at time t * * ::::::::::: * :::: : :::: * ::: : ::: * :: R₀ : R₁ :: * :............:...........: * x - - - - - - -> :: . : :: - - - - - - -> y * ::: ° : ° ::: * :::: : . ::::: * ::::::::::: * * @notice Deposit is the entry point for entering the Kiru bonding-curve * upon receiving ETH, the kiru contract computes the amount out `y` * using the [skew trading function](https://github.com/RedKiruMoney/Kiru). * The result is an asymmetric bonding curve that can optimise for price * appreciation, biased by the skew factor α(t). * * The skew factor α(t) is a continuous function that dictates the * evolution of the market's reserves asymmetry through time, decreasing * as the ratio R₁(t) / R₁(0) decreases. * * ^ * | : * | :: * | ::: * | :::: * | ::::: * | :::::: * | ::::::::: * | ::::::::::::: * ∘-------------------------------------> * * @dev The skew factor α(t) is a continuous function of the markets reserves * computed as α(t) = 1 − α(0) ⋅ (R₁(0) / R₁(t)) * The market simulates a lp burn of α(t) right before a buy. * The skew factor α(t) represents the fraction of the current reserves that * the LP is withdrawing. * We have the trading function (x, R₀, R₁) -> y : * * αK = αR₀ * αR₁ * αR₁′ = αK / (αR₀ + x) * y = αR₁ - αR₁′ * * After computing the swapped amount `y` on cut reserves, the LP provides the * maximum possible liquidity from the withdrawn amount at the new market rate: * * spot = R₁ / R₀ * R₀′ = R₀ + x * R₁′ = (R₀ * R₁) / R₀′ * * @param out The minimum amount of KIRU to receive. * This parameter introduces slippage bounds for the trade. */ function deposit(uint out) external payable { uint _ALPHA_INIT = ALPHA_INIT; uint _R1_INIT = R1_INIT; bytes32 _SELF_BALANCE_SLOT = SELF_BALANCE_SLOT; bytes32 _BURN_BALANCE_SLOT = BURN_BALANCE_SLOT; bytes32 _TRANSFER_EVENT_SIG = TRANSFER_EVENT_SIG; bytes32 _BUY_EVENT_SIG = BUY_EVENT_SIG; assembly { let ptr := mload(0x40) let chad := caller() /* * Load balanceOf[msg.sender] slot */ mstore(ptr, chad) mstore(add(ptr, 0x20), 0) let CALLER_BALANCE_SLOT := keccak256(ptr, 0x40) /* * x = msg.value * R₀ = token₀ reserves * R₁ = token₁ reserves */ let x := callvalue() let r0 := sub(sub(selfbalance(), sload(2)), callvalue()) let r1 := sload(_SELF_BALANCE_SLOT) /* * Compute the skew factor α(t) if activated */ let r1InitR1Ratio := mul(div(mul(r1, 1000000), _R1_INIT), gt(_R1_INIT, r1)) let inverseAlpha := div(mul(_ALPHA_INIT, r1InitR1Ratio), 1000000) let alpha := sub(1000000, inverseAlpha) /* * α-reserves: reserves cut by the skew factor α(t) */ let alphaR0 := div(mul(alpha, r0), 1000000) let alphaR1 := div(mul(alpha, r1), 1000000) /* * The new α-reserve 0, increased by x * * αR₀′ = αR₀ + x */ let alphaR0Prime := add(alphaR0, x) /* * The new α-reserve 1, computed as the constant product of the α-reserves * * αR₁′ = (αR₀ * αR₁) / αR₀′ */ let alphaR1Prime := div(mul(alphaR0, alphaR1), alphaR0Prime) /* * The amount out y, computed as the difference between the initial α-reserve 1 and the new α-reserve 1 * * y = αR₁ - αR₁′ */ let y := sub(alphaR1, alphaR1Prime) /* * The new actual reserve 0, increased by x * * R₀′ = R₀ + x */ let r0Prime := add(r0, x) /* * The new actual reserve 1, computed as new reserve 0 * α-ratio * where α-ratio: αR₁′ / αR₀′ * * R₁′ = (αR₁′ / αR₀′) * R₀′ + ϵ */ let r1Prime := add(div(mul(div(mul(alphaR1Prime, 1000000), alphaR0Prime), r0Prime), 1000000), 1) /* * Delta between constant product of actual reserves and new reserve 1 * * ΔR₁ = R₁ - R₁′ - y */ let deltaToken1 := sub(sub(r1, r1Prime), y) /* * Ensures that the amount out is within slippage bounds */ if gt(out, y) {revert(0, 0)} /* * Update the market reserves to (R₀′, R₁′) then update balances */ sstore(_SELF_BALANCE_SLOT, r1Prime) let balanceOfCaller := sload(CALLER_BALANCE_SLOT) sstore(CALLER_BALANCE_SLOT, add(balanceOfCaller, y)) let balanceOfBurn := sload(_BURN_BALANCE_SLOT) sstore(_BURN_BALANCE_SLOT, add(balanceOfBurn, deltaToken1)) /* * decrease total supply */ sstore(3, sub(sload(3), deltaToken1)) /* * emit Transfer event */ mstore(add(ptr, 0x20), y) log3(add(ptr, 0x20), 0x20, _TRANSFER_EVENT_SIG, address(), chad) /* * emit Buy event */ mstore(ptr, x) mstore(add(ptr, 0x20), y) mstore(add(ptr, 0x40), r0Prime) mstore(add(ptr, 0x60), r1Prime) log2(ptr, 0x80, _BUY_EVENT_SIG, chad) } } ////////////////////////////////////////////////////////////// /////////////////////////// Sell ///////////////////////////// ////////////////////////////////////////////////////////////// /** * * _____ * .'/L|__`. * / =[_]O|` \\ * | _ | | * ; (_) ; | * '.___.' \\ \\ * | | \\ `-. * | _| `\\ \\ * _./' \\._ `-._\\ * .'/ `.`. '_.-. * * @notice Withdraws ETH from Jita's bonding curve * * @dev This contracts operate at low-level and does not require sending funds * before withdrawing, it instead requires the caller to have enough * balance, then balance slots gets updated accordingly. * * @param value The amount of KIRU to sell. * * @param out The minimum amount of ETH to receive. * This parameter introduces slippage bounds for the trade. */ function withdraw(uint value, uint out) external { uint _PHI_FACTOR = PHI_FACTOR; bytes32 _SELF_BALANCE_SLOT = SELF_BALANCE_SLOT; bytes32 _TRANSFER_EVENT_SIG = TRANSFER_EVENT_SIG; bytes32 _SELL_EVENT_SIG = SELL_EVENT_SIG; assembly { let ptr := mload(0x40) /* * Load balanceOf[msg.sender] slot */ let jeet := caller() mstore(ptr, jeet) mstore(add(ptr, 0x20), 0) let FROM_BALANCE_SLOT := keccak256(ptr, 0x40) /* * check that caller has enough funds */ let balanceFrom := sload(FROM_BALANCE_SLOT) if lt(balanceFrom, value) { revert(0, 0) } let _cumulatedFees := sload(2) /* * load market's reserves (R₀, R₁) * * r₀ = ETH balance - cumulated fees * r₁ = KIRU balance */ let r0 := sub(selfbalance(), _cumulatedFees) let r1 := sload(_SELF_BALANCE_SLOT) let y := value /* * Only allow sell orders under 5% of Kiru's liquidity * to prevent divergence. */ if gt(y, div(r1, 5)) { revert(0, 0) } /* * the kiru sent is sold for ETH using the CP formula: * * y = (R₁ - K / (R₀ + x)) * ϕfactor * * where: * * y: Kiru amount in * x: Eth amount out * K: R₀ * R₁ * ϕfactor: withdrawal penalty factor * * Compute the new reserve 1 by adding the amount out y * * R₁′ = R₁ + y */ let r1Prime := add(r1, y) /* * R₀′ = K / R₁′ */ let r0Prime := div(mul(r0, r1), r1Prime) /* * Compute the raw amount out x * * x = R₀ - R₀′ */ let raw_x := sub(r0, r0Prime) /* * Compute the withdrawal fee ϕ * * ϕ = x * ϕfactor */ let phi := div(mul(raw_x, _PHI_FACTOR), 1000000) /* * Compute the amount out x * * x = x - ϕ */ let x := sub(raw_x, phi) /* * Ensures that the amount out is within slippage bounds */ if gt(out, x) {revert(0, 0)} /* * increment cumulated fees */ sstore(2, add(_cumulatedFees, phi)) /* * decrease sender's balance */ sstore(FROM_BALANCE_SLOT, sub(balanceFrom, value)) /* * increase bonding curve balance */ sstore(_SELF_BALANCE_SLOT, add(sload(_SELF_BALANCE_SLOT), value)) /* * Transfer ETH to the seller's address */ if iszero(call(gas(), jeet, x, 0, 0, 0, 0)) { revert(0, 0) } /* * emit Transfer event */ mstore(ptr, value) log3(ptr, 0x20, _TRANSFER_EVENT_SIG, jeet, address()) /* * emit Sell event */ mstore(ptr, value) mstore(add(ptr, 0x20), x) mstore(add(ptr, 0x40), r0Prime) mstore(add(ptr, 0x60), r1Prime) log2(ptr, 0x80, _SELL_EVENT_SIG, jeet) } } ////////////////////////////////////////////////////////////// ////////////////////////// Quote ///////////////////////////// ////////////////////////////////////////////////////////////// /** * @notice Computes the amount of KIRU received for a given amount of ETH deposited * given the current market reserves. * * @param value The amount of ETH to deposit. * * @return The amount of KIRU received. */ function quoteDeposit(uint value) public view returns (uint) { uint _ALPHA_INIT = ALPHA_INIT; uint _R1_INIT = R1_INIT; bytes32 _SELF_BALANCE_SLOT = SELF_BALANCE_SLOT; assembly { let ptr := mload(0x40) let r0 := sub(selfbalance(), sload(2)) let r1 := sload(_SELF_BALANCE_SLOT) let r1InitR1Ratio := mul(div(mul(r1, 1000000), _R1_INIT), gt(_R1_INIT, r1)) let inverseAlpha := div(mul(_ALPHA_INIT, r1InitR1Ratio), 1000000) let alpha := sub(1000000, inverseAlpha) let alphaR0 := div(mul(alpha, r0), 1000000) let alphaR1 := div(mul(alpha, r1), 1000000) let alphaR0Prime := add(alphaR0, value) let alphaR1Prime := div(mul(alphaR0, alphaR1), alphaR0Prime) let y := sub(alphaR1, alphaR1Prime) mstore(ptr, y) return(ptr, 0x20) } } /** * @notice Computes the amount of ETH received for a given amount of KIRU withdrawn * given the current market reserves. * * @param value The amount of KIRU to withdraw. * * @return The amount of ETH received. */ function quoteWithdraw(uint value) public view returns (uint) { uint _PHI_FACTOR = PHI_FACTOR; bytes32 _SELF_BALANCE_SLOT = SELF_BALANCE_SLOT; assembly { let ptr := mload(0x40) let r0 := sub(selfbalance(), sload(2)) let r1 := sload(_SELF_BALANCE_SLOT) let r1Prime := add(r1, value) let x := sub(r0, div(mul(r0, r1), r1Prime)) let phi := div(mul(x, _PHI_FACTOR), 1000000) let xOut := sub(x, phi) mstore(ptr, xOut) return(ptr, 0x20) } } ////////////////////////////////////////////////////////////// /////////////////////////// View ///////////////////////////// ////////////////////////////////////////////////////////////// /** * @notice Returns the market's reserves and the skew factor α(t). * * @return r0 The balance of the KIRU contract. * * @return r1 The balance of the KIRU contract. * * @return alpha The skew factor α(t). */ function getState() public view returns (uint r0, uint r1, uint alpha) { uint _ALPHA_INIT = ALPHA_INIT; uint _R1_INIT = R1_INIT; bytes32 _SELF_BALANCE_SLOT = SELF_BALANCE_SLOT; assembly { r0 := sub(selfbalance(), sload(2)) r1 := sload(_SELF_BALANCE_SLOT) let r1InitR1Ratio := mul(div(mul(r1, 1000000), _R1_INIT), gt(_R1_INIT, r1)) let inverseAlpha := div(mul(_ALPHA_INIT, r1InitR1Ratio), 1000000) alpha := sub(1000000, inverseAlpha) } } ////////////////////////////////////////////////////////////// //////////////////////////// KIRU //////////////////////////// ////////////////////////////////////////////////////////////// /** * @notice Returns the balance of the specified address. * * @param to The address to get the balance of. * * @return _balance The balance of the specified address. */ function balanceOf(address to) public view returns (uint _balance) { assembly { let ptr := mload(0x40) /* * Load balanceOf[to] slot */ mstore(ptr, to) mstore(add(ptr, 0x20), 0) let TO_BALANCE_SLOT := keccak256(ptr, 0x40) /* * Load balanceOf[to] */ _balance := sload(TO_BALANCE_SLOT) } } /** * @notice Returns the amount of tokens that the spender is allowed to transfer from the owner. * * @param owner The address of the owner. * * @param spender The address of the spender. * * @return __allowance The amount of tokens that the spender is allowed to transfer from the owner. */ function allowance(address owner, address spender) public view returns (uint __allowance) { assembly { let ptr := mload(0x40) /* * Load allowance[owner][spender] slot */ mstore(ptr, owner) mstore(add(ptr, 0x20), 1) let OWNER_ALLOWANCE_SLOT := keccak256(ptr, 0x40) mstore(ptr, spender) mstore(add(ptr, 0x20), OWNER_ALLOWANCE_SLOT) let SPENDER_ALLOWANCE_SLOT := keccak256(ptr, 0x40) /* * Load allowance[owner][spender] */ __allowance := sload(SPENDER_ALLOWANCE_SLOT) } } /** * @notice Efficient ERC20 transfer function * * @dev The caller must have sufficient balance to transfer the specified amount. * * @param to The address to transfer to. * * @param value The amount of kiru to transfer. * * @return true */ function transfer(address to, uint value) public returns (bool) { bytes32 _TRANSFER_EVENT_SIG = TRANSFER_EVENT_SIG; assembly { let ptr := mload(0x40) /* * Load balanceOf[msg.sender] slot */ let from := caller() mstore(ptr, from) mstore(add(ptr, 0x20), 0) let FROM_BALANCE_SLOT := keccak256(ptr, 0x40) /* * Load balanceOf[to] slot */ mstore(ptr, to) let TO_BALANCE_SLOT := keccak256(ptr, 0x40) /* * Ensures that caller has enough balance */ let balanceFrom := sload(FROM_BALANCE_SLOT) if lt(balanceFrom, value) { revert(0, 0) } /* * decrease sender's balance */ sstore(FROM_BALANCE_SLOT, sub(balanceFrom, value)) /* * increment recipient's balance */ sstore(TO_BALANCE_SLOT, add(sload(TO_BALANCE_SLOT), value)) /* * emit Transfer event */ mstore(ptr, value) log3(ptr, 0x20, _TRANSFER_EVENT_SIG, from, to) /* * return true */ mstore(ptr, 1) return(ptr, 0x20) } } /** * @notice Efficient ERC20 transferFrom function * * @dev The caller must have sufficient allowance to transfer the specified amount. * From must have sufficient balance to transfer the specified amount. * * @param from The address to transfer from. * * @param to The address to transfer to. * * @param value The amount of kiru to transfer. * * @return true */ function transferFrom(address from, address to, uint value) public returns (bool) { bytes32 _TRANSFER_EVENT_SIG = TRANSFER_EVENT_SIG; assembly { let ptr := mload(0x40) /* * Load balanceOf[from] slot */ let spender := caller() mstore(ptr, from) mstore(add(ptr, 0x20), 0) let FROM_BALANCE_SLOT := keccak256(ptr, 0x40) /* * Load balanceOf[to] slot */ mstore(ptr, to) let TO_BALANCE_SLOT := keccak256(ptr, 0x40) /* * Load allowance[from][spender] slot */ mstore(ptr, from) mstore(add(ptr, 0x20), 1) let ALLOWANCE_FROM_SLOT := keccak256(ptr, 0x40) mstore(ptr, spender) mstore(add(ptr, 0x20), ALLOWANCE_FROM_SLOT) let ALLOWANCE_FROM_CALLER_SLOT := keccak256(ptr, 0x40) /* * check that msg.sender has sufficient allowance */ let __allowance := sload(ALLOWANCE_FROM_CALLER_SLOT) if lt(__allowance, value) { revert(0, 0) } /* * Reduce the spender's allowance */ sstore(ALLOWANCE_FROM_CALLER_SLOT, sub(__allowance, value)) /* * Ensures that from has enough funds */ let balanceFrom := sload(FROM_BALANCE_SLOT) if lt(balanceFrom, value) { revert(0, 0) } /* * Decrease sender's balance */ sstore(FROM_BALANCE_SLOT, sub(balanceFrom, value)) /* * Increment recipient's balance */ sstore(TO_BALANCE_SLOT, add(sload(TO_BALANCE_SLOT), value)) /* * Emit Transfer event */ mstore(ptr, value) log3(ptr, 0x20, _TRANSFER_EVENT_SIG, from, to) /* * Return true */ mstore(ptr, 1) return(ptr, 0x20) } } /** * @notice Approves a spender to transfer KIRU tokens on behalf of the caller * This function implements the ERC20 approve functionality, allowing * delegation of token transfers to other addresses. * * @dev This function sets the allowance that the spender can transfer from * the caller's balance. The allowance can be increased or decreased * by calling this function again with a different value. * Note that setting a non-zero allowance from a non-zero value should * typically be done by first setting it to zero to prevent front-running. * * @param to The address being approved to spend tokens * * @param value The amount of tokens the spender is approved to transfer * * @return bool Returns true if the approval was successful */ function approve(address to, uint value) public returns (bool) { bytes32 _APPROVAL_EVENT_SIG = APPROVAL_EVENT_SIG; assembly { let ptr := mload(0x40) /* * Load allowance[owner][spender] slot */ let owner := caller() mstore(ptr, owner) mstore(add(ptr, 0x20), 1) let ALLOWANCE_CALLER_SLOT := keccak256(ptr, 0x40) mstore(ptr, to) mstore(add(ptr, 0x20), ALLOWANCE_CALLER_SLOT) let ALLOWANCE_CALLER_TO_SLOT := keccak256(ptr, 0x40) /* * Store the new allowance value */ sstore(ALLOWANCE_CALLER_TO_SLOT, value) /* * Emit Approval event */ mstore(ptr, value) log3(ptr, 0x20, _APPROVAL_EVENT_SIG, owner, to) /* * Return true */ mstore(ptr, 1) return(ptr, 0x20) } } /** * @notice Burns KIRU tokens from the caller's balance * The burned tokens are permanently removed from circulation, * reducing the total supply. * * @dev This function requires the caller to have sufficient balance. * The tokens are burned by transferring them to the zero address * and decreasing the total supply. * * @param value The amount of KIRU tokens to burn */ function burn(uint value) public { bytes32 _TRANSFER_EVENT_SIG = TRANSFER_EVENT_SIG; assembly { let ptr := mload(0x40) let from := caller() mstore(ptr, from) mstore(add(ptr, 0x20), 0) let FROM_BALANCE_SLOT := keccak256(ptr, 0x40) let balanceFrom := sload(FROM_BALANCE_SLOT) if lt(balanceFrom, value) { revert(0, 0) } sstore(FROM_BALANCE_SLOT, sub(balanceFrom, value)) sstore(3, sub(sload(3), value)) mstore(ptr, value) log3(ptr, 0x20, _TRANSFER_EVENT_SIG, from, 0) } } /** * @notice Returns the total supply of KIRU tokens in circulation * @dev This value decreases when tokens are burned and represents the current * amount of KIRU tokens that exist * @return The total number of KIRU tokens in circulation */ function totalSupply() public view returns (uint) { return _totalSupply; } /** * @notice Collects the cumulated withdraw fees and transfers them to the treasury. */ function collect() external { address _TREASURY = TREASURY; assembly { let _cumulatedFees := sload(2) // set cumulated fees to 0 sstore(2, 0) // transfer cumulated fees to treasury if iszero(call(gas(), _TREASURY, _cumulatedFees, 0, 0, 0, 0)) { revert (0, 0)} } } receive() external payable {} }