Code Monkey home page Code Monkey logo

cairo-math-64x61's People

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar

cairo-math-64x61's Issues

toUint256() function should return the number without the floating part

Thanks to the great lib.

I just have one remark.
I expected the toUint256() function to return the number rounded as Uint256.
For example: passing 12,3456 I expect to get 12 from toUint256()
To get the right result I have to pass my 12,3456 into the toFelt() function and then the toUint256().

Also we can add what type of rounding we want as param to the function.

Best,

Allow for non starknet

If you remove the %lang starknet from the pure cairo files, it can be reusable in pure cairo.

Add 'unsafe' versions of each function

Math64x61_assert64x61 is expensive! I was able to reduce the step count of a function by close to 60% just by getting rid of that assert where it wasn't necessary (meaning the fixed-point operations were guaranteed to not overflow).

Of course these should be used very carefully, but I think they'd be a useful addition.

A bug in the function`ceil`

If the input of the ceil function is an integer x(that is, x modulo FRACT_PART is equal to 0), it will return x+1. But I think this does not match the usual semantics of ceil function in mathematics.

The bug is found by Medjai, a symbolic execution tool for the Cairo program. The spec we write for ceil is:

func ceil_spec{range_check_ptr}():
    alloc_locals
    let (local x) = SymbolicMath64x61()
    let (local res) = Math64x61.ceil(x)
    verify_le_signed(x, res)
    verify_lt_signed(res - Math64x61.ONE, x)
    let (_, rem) = signed_div_rem(res, Math64x61.ONE, Math64x61.BOUND)
    medjai_assert_eq_felt(rem, 0)
    return ()
end

Rounding bug when using library

Hi, we have a precision bug when converting felts with the library and would like to get some help.

We have a fee rate that we store globally and per account. The value of the fee rate is 0.0004 but before the value is sent to the contract function we add some quantum precision to it by doing (i.e 0.0004 * 10^8) and then send it as 40000. In the function where the fee rate is used, we remove this precision by doing a Math.to_decimal8(feeRate). All Math.to_decimal8(feeRate) does is remove the quantum precision before the fee rate is stored in storage.

To further illustrate this, when we try to retrieve the fee rate stored, instead of getting 40000, the value returned is 39999. But it does not stop there. We also noticed that when the fee rate is a multiple of 5, the exact value is returned with no change in precision. Eg 0.0005 (or 50000) returns 50000. But any other value that is not a multiple of 5 loses precision.

A code sample to demonstrate this is attached below.

// SPDX-License-Identifier: Apache-2.0
%lang starknet

from cairo_math_64x61.math64x61 import Math64x61
from starkware.cairo.common.bool import TRUE, FALSE
from starkware.cairo.common.cairo_builtins import HashBuiltin

struct FeeRate {
    exists: felt,
    maker: felt,
    taker: felt,
}

@storage_var
func global_fee_rate() -> (feeRate: FeeRate) {
}

@storage_var
func account_fee_rate(account: felt) -> (feeRate: FeeRate) {
}

namespace Math {
    const DOT8 = (10 ** 8) * Math64x61.FRACT_PART;
    func to_decimal8{range_check_ptr}(num: felt) -> felt {
       alloc_locals;
       // To fixed precision
       local _ans = Math64x61.fromFelt(num);
       // Remove quantum precision
       let ans = Math64x61.div(_ans, DOT8);
       return ans;
    }

    func to_felt8{range_check_ptr}(num: felt) -> felt {
        // Add quantum precision
        let _ans =  Math64x61.mul(num, DOT8);
        // Remove fixed precision
        let ans = Math64x61.toFelt(_ans);
        return ans;
    }
}

@external
func convertAndSetGlobalFeeRate{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(
    maker_fee: felt, taker_fee: felt
) {
    alloc_locals;

    let maker_fee_d = Math.to_decimal8(maker_fee);
    let taker_fee_d = Math.to_decimal8(taker_fee);

    let fee_rate_d = FeeRate(exists=1, maker=maker_fee_d, taker=taker_fee_d);
    global_fee_rate.write(fee_rate_d);

    return ();
}

@view
func convertAndGetAccountFeeRate{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(
    account: felt) -> (fee_rate: FeeRate) {
    alloc_locals;
    let (_account_fee_rate) = account_fee_rate.read(account);

    if (_account_fee_rate.exists != FALSE) {
        return (_account_fee_rate,);
    }

    // Return global fee rate if account fee rate is not set
    let (_global_fee_rate) = global_fee_rate.read();
    let maker_fee = Math.to_felt8(_global_fee_rate.maker);
    let taker_fee = Math.to_felt8(_global_fee_rate.taker);

    let fee_rate = FeeRate(
        exists=1,
        maker=maker_fee,
        taker=taker_fee,
    );
    return (fee_rate,);
}

Here's a test case that shows that converting the fee_rate to decimal_8 before storage and after retrieval

import dataclasses
import os

import pytest
from starkware.starknet.testing.starknet import Starknet
from starkware.starkware_utils.error_handling import StarkException

from .types import TokenAsset
from .utils import str_to_felt, to_quantum

FILE_DIR = os.path.dirname(__file__)
CAIRO_PATH = [os.path.join(FILE_DIR, "../contracts")]
ROUNDING_FILE = os.path.join(FILE_DIR, "../contracts/test/Rounding.cairo")
ACCOUNT_ADDRESS = str_to_felt("ACCOUNT ADDRESS")

@pytest.fixture()
async def contracts():
    starknet = await Starknet.empty()
    rounding_contract = await starknet.deploy(
        source=ROUNDING_FILE, cairo_path=CAIRO_PATH, disable_hint_validation=True
    )

    return rounding_contract

async def test_fee_rate_with_rounding(contracts):
    rounding_contract = contracts
    maker_fee_rate = to_quantum(0.0001)
    taker_fee_rate = to_quantum(0.0004)
    await rounding_contract.convertAndSetGlobalFeeRate(
        maker_fee_rate,
        taker_fee_rate
    ).execute()

    account_fee_rates = await rounding_contract.convertAndGetAccountFeeRate(ACCOUNT_ADDRESS).call()
    assert not (account_fee_rates.result.fee_rate.maker == maker_fee_rate)
    assert not (account_fee_rates.result.fee_rate.taker == taker_fee_rate)

I will be happy to provide further information to help debug this issue. Thanks!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.