Skip to content

Commit

Permalink
[protocol 3] Addded option for increased trading fees (#2335)
Browse files Browse the repository at this point in the history
  • Loading branch information
Brechtpd authored Apr 20, 2021
1 parent 5d8542e commit 23ffef0
Show file tree
Hide file tree
Showing 10 changed files with 211 additions and 47 deletions.
2 changes: 2 additions & 0 deletions packages/loopring_v3.js/src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ export class Constants {

static readonly MAX_AMOUNT = new BN(2).pow(new BN(96)).sub(new BN(1));

static readonly FEE_MULTIPLIER = 50;

static readonly Float24Encoding: FloatEncoding = {
numBitsExponent: 5,
numBitsMantissa: 19,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,11 +65,17 @@ export class SpotTradeProcessor {

// Further extraction of packed data
const limitMaskA = orderDataA & 0b10000000;
const feeBipsA = orderDataA & 0b00111111;
const feeBipsMultiplierFlagA = orderDataA & 0b01000000;
const feeBipsA =
(orderDataA & 0b00111111) *
(feeBipsMultiplierFlagA ? Constants.FEE_MULTIPLIER : 1);
const fillAmountBorSA = limitMaskA > 0;

const limitMaskB = orderDataB & 0b10000000;
const feeBipsB = orderDataB & 0b00111111;
const feeBipsMultiplierFlagB = orderDataB & 0b01000000;
const feeBipsB =
(orderDataB & 0b00111111) *
(feeBipsMultiplierFlagB ? Constants.FEE_MULTIPLIER : 1);
const fillAmountBorSB = limitMaskB > 0;

// Decode the float values
Expand Down
8 changes: 4 additions & 4 deletions packages/loopring_v3/circuit/Circuits/SpotTradeCircuit.h
Original file line number Diff line number Diff line change
Expand Up @@ -346,11 +346,11 @@ class SpotTradeCircuit : public BaseTransactionCircuit
fillS_B.bits(),

orderA.fillAmountBorS.bits,
VariableArrayT(1, state.constants._0),
orderA.feeBips.bits,
orderA.feeBipsMultiplierFlag.bits,
orderA.feeBipsData.bits,
orderB.fillAmountBorS.bits,
VariableArrayT(1, state.constants._0),
orderB.feeBips.bits,
orderB.feeBipsMultiplierFlag.bits,
orderB.feeBipsData.bits,
});
}
};
Expand Down
12 changes: 5 additions & 7 deletions packages/loopring_v3/circuit/Gadgets/MathGadgets.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,7 @@ namespace Loopring

// All Poseidon permutations used
using Poseidon_2 = Poseidon_gadget_T<3, 1, 6, 51, 2, 1>;
template <unsigned n_outputs>
using Poseidon_4_ = Poseidon_gadget_T<5, 1, 6, 52, n_outputs, 1>;
template <unsigned n_outputs> using Poseidon_4_ = Poseidon_gadget_T<5, 1, 6, 52, n_outputs, 1>;
using Poseidon_4 = Poseidon_4_<4>;
using Poseidon_5 = Poseidon_gadget_T<6, 1, 6, 52, 5, 1>;
using Poseidon_6 = Poseidon_gadget_T<7, 1, 6, 52, 6, 1>;
Expand Down Expand Up @@ -70,6 +69,7 @@ class Constants : public GadgetT
const VariableT txTypeSpotTrade;
const VariableT txTypeTransfer;
const VariableT txTypeWithdrawal;
const VariableT feeMultiplier;

const VariableArrayT zeroAccount;

Expand Down Expand Up @@ -105,6 +105,7 @@ class Constants : public GadgetT
make_variable(pb, ethsnarks::FieldT(int(TransactionType::Transfer)), FMT(prefix, ".txTypeTransfer"))),
txTypeWithdrawal(
make_variable(pb, ethsnarks::FieldT(int(TransactionType::Withdrawal)), FMT(prefix, ".txTypeWithdrawal"))),
feeMultiplier(make_variable(pb, ethsnarks::FieldT(FEE_MULTIPLIER), FMT(prefix, ".feeMultiplier"))),

zeroAccount(NUM_BITS_ACCOUNT, _0)
{
Expand Down Expand Up @@ -161,13 +162,14 @@ class Constants : public GadgetT
pb.add_r1cs_constraint(
ConstraintT(txTypeWithdrawal, FieldT::one(), ethsnarks::FieldT(int(TransactionType::Withdrawal))),
".txTypeWithdrawal");
pb.add_r1cs_constraint(
ConstraintT(feeMultiplier, FieldT::one(), ethsnarks::FieldT(FEE_MULTIPLIER)), ".feeMultiplier");
}
};

class DualVariableGadget : public libsnark::dual_variable_gadget<FieldT>
{
public:

DualVariableGadget( //
ProtoboardT &pb,
const size_t width,
Expand Down Expand Up @@ -205,15 +207,13 @@ class DualVariableGadget : public libsnark::dual_variable_gadget<FieldT>
class ToBitsGadget : public libsnark::dual_variable_gadget<FieldT>
{
public:

ToBitsGadget( //
ProtoboardT &pb,
const VariableT &value,
const size_t width,
const std::string &prefix)
: libsnark::dual_variable_gadget<FieldT>(pb, value, width, prefix)
{

}

void generate_r1cs_witness()
Expand All @@ -232,14 +232,12 @@ typedef ToBitsGadget RangeCheckGadget;
class FromBitsGadget : public libsnark::dual_variable_gadget<FieldT>
{
public:

FromBitsGadget( //
ProtoboardT &pb,
const VariableArrayT &bits,
const std::string &prefix)
: libsnark::dual_variable_gadget<FieldT>(pb, bits, prefix)
{

}

void generate_r1cs_witness()
Expand Down
47 changes: 47 additions & 0 deletions packages/loopring_v3/circuit/Gadgets/OrderGadgets.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,20 @@ class OrderGadget : public GadgetT
DualVariableGadget feeBips;
DualVariableGadget amm;

DualVariableGadget feeBipsMultiplierFlag;
DualVariableGadget feeBipsData;

NotGadget notAmm;

// Checks
RequireLeqGadget feeBips_leq_maxFeeBips;
RequireNotEqualGadget tokenS_neq_tokenB;
RequireNotZeroGadget amountS_notZero;
RequireNotZeroGadget amountB_notZero;
// Fee checks
TernaryGadget feeMultiplier;
UnsafeMulGadget decodedFeeBips;
RequireEqualGadget feeBipsEqualsDecodedFeeBips;

// Signature
Poseidon_11 hash;
Expand All @@ -68,6 +75,9 @@ class OrderGadget : public GadgetT
feeBips(pb, NUM_BITS_BIPS, FMT(prefix, ".feeBips")),
amm(pb, 1, FMT(prefix, ".amm")),

feeBipsMultiplierFlag(pb, 1, FMT(prefix, ".feeBipsMultiplierFlag")),
feeBipsData(pb, NUM_BITS_BIPS_DA, FMT(prefix, ".feeBipsData")),

notAmm(pb, amm.packed, FMT(prefix, ".notAmm")),

// Checks
Expand All @@ -80,6 +90,19 @@ class OrderGadget : public GadgetT
tokenS_neq_tokenB(pb, tokenS.packed, tokenB.packed, FMT(prefix, ".tokenS != tokenB")),
amountS_notZero(pb, amountS.packed, FMT(prefix, ".amountS != 0")),
amountB_notZero(pb, amountB.packed, FMT(prefix, ".amountB != 0")),
// Fee checks
feeMultiplier(
pb,
feeBipsMultiplierFlag.packed,
constants.feeMultiplier,
constants._1,
FMT(prefix, ".feeMultiplier")),
decodedFeeBips(pb, feeBipsData.packed, feeMultiplier.result(), FMT(prefix, ".decodedFeeBips")),
feeBipsEqualsDecodedFeeBips(
pb,
feeBips.packed,
decodedFeeBips.result(),
FMT(prefix, ".feeBipsEqualsDecodedFeeBips")),

// Signature
hash(
Expand Down Expand Up @@ -117,13 +140,30 @@ class OrderGadget : public GadgetT
feeBips.generate_r1cs_witness(pb, order.feeBips);
amm.generate_r1cs_witness(pb, order.amm);

// Use the fee multiplier if necessary
if (toBigInt(order.feeBips) >= 64 /*2**NUM_BITS_BIPS_DA*/)
{
feeBipsMultiplierFlag.generate_r1cs_witness(pb, ethsnarks::FieldT(1));
feeBipsData.generate_r1cs_witness(
pb, ethsnarks::FieldT((toBigInt(order.feeBips) / FEE_MULTIPLIER).to_int()));
}
else
{
feeBipsMultiplierFlag.generate_r1cs_witness(pb, ethsnarks::FieldT(0));
feeBipsData.generate_r1cs_witness(pb, order.feeBips);
}

notAmm.generate_r1cs_witness();

// Checks
feeBips_leq_maxFeeBips.generate_r1cs_witness();
tokenS_neq_tokenB.generate_r1cs_witness();
amountS_notZero.generate_r1cs_witness();
amountB_notZero.generate_r1cs_witness();
// Fee checks
feeMultiplier.generate_r1cs_witness();
decodedFeeBips.generate_r1cs_witness();
feeBipsEqualsDecodedFeeBips.generate_r1cs_witness();

// Signature
hash.generate_r1cs_witness();
Expand All @@ -145,13 +185,20 @@ class OrderGadget : public GadgetT
feeBips.generate_r1cs_constraints(true);
amm.generate_r1cs_constraints(true);

feeBipsMultiplierFlag.generate_r1cs_constraints(true);
feeBipsData.generate_r1cs_constraints(true);

notAmm.generate_r1cs_constraints();

// Checks
feeBips_leq_maxFeeBips.generate_r1cs_constraints();
tokenS_neq_tokenB.generate_r1cs_constraints();
amountS_notZero.generate_r1cs_constraints();
amountB_notZero.generate_r1cs_constraints();
// Fee checks
feeMultiplier.generate_r1cs_constraints();
decodedFeeBips.generate_r1cs_constraints();
feeBipsEqualsDecodedFeeBips.generate_r1cs_constraints();

// Signature
hash.generate_r1cs_constraints();
Expand Down
3 changes: 2 additions & 1 deletion packages/loopring_v3/circuit/Gadgets/SignatureGadgets.h
Original file line number Diff line number Diff line change
Expand Up @@ -372,7 +372,8 @@ class SignatureVerifier : public GadgetT
{
for (unsigned int i = 0; i < sig_s.size(); i++)
{
libsnark::generate_boolean_r1cs_constraint<ethsnarks::FieldT>(pb, sig_s[i], FMT(annotation_prefix, ".bitness"));
libsnark::generate_boolean_r1cs_constraint<ethsnarks::FieldT>(
pb, sig_s[i], FMT(annotation_prefix, ".bitness"));
}
signatureVerifier.generate_r1cs_constraints();
valid.generate_r1cs_constraints();
Expand Down
4 changes: 3 additions & 1 deletion packages/loopring_v3/circuit/Utils/Constants.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ static const unsigned int NUM_BITS_TOKEN = TREE_DEPTH_TOKENS * 2;
static const unsigned int NUM_BITS_STORAGEID = 32;
static const unsigned int NUM_BITS_TIMESTAMP = 32;
static const unsigned int NUM_BITS_NONCE = 32;
static const unsigned int NUM_BITS_BIPS = 6;
static const unsigned int NUM_BITS_BIPS = 12; // ceil(log2(2**NUM_BITS_BIPS_DA * FEE_MULTIPLIER))
static const unsigned int NUM_BITS_BIPS_DA = 6;
static const unsigned int NUM_BITS_PROTOCOL_FEE_BIPS = 8;
static const unsigned int NUM_BITS_TYPE = 8;
static const unsigned int NUM_STORAGE_SLOTS = 16384; // 2**NUM_BITS_STORAGE_ADDRESS
Expand All @@ -35,6 +36,7 @@ static const char *EMPTY_TRADE_HISTORY = "65927491675782344981534105642433692294
static const char *MAX_AMOUNT = "79228162514264337593543950335"; // 2^96 - 1
static const char *FIXED_BASE = "1000000000000000000"; // 10^18
static const unsigned int NUM_BITS_FIXED_BASE = 60; // ceil(log2(10^18))
static const unsigned int FEE_MULTIPLIER = 50;

struct FloatEncoding
{
Expand Down
35 changes: 33 additions & 2 deletions packages/loopring_v3/circuit/test/OrderTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -101,16 +101,47 @@ TEST_CASE("Order", "[OrderGadget]")
SECTION("feeBips > maxFeeBips")
{
Order _order = order;
for (unsigned int maxFeeBips = 0; maxFeeBips < pow(2, NUM_BITS_BIPS); maxFeeBips += 3)
for (unsigned int maxFeeBips = 0; maxFeeBips < pow(2, NUM_BITS_BIPS_DA); maxFeeBips += 3)
{
for (unsigned int feeBips = 0; feeBips < pow(2, NUM_BITS_BIPS); feeBips += 3)
for (unsigned int feeBips = 0; feeBips < pow(2, NUM_BITS_BIPS_DA); feeBips += 3)
{
_order.maxFeeBips = maxFeeBips;
_order.feeBips = feeBips;
bool expectedSatisfied = (feeBips <= maxFeeBips);
orderChecked(exchange, _order, expectedSatisfied);
}
}
unsigned int feeBipsLimit = pow(2, NUM_BITS_BIPS_DA) * FEE_MULTIPLIER;
for (unsigned int maxFeeBips = 0; maxFeeBips < feeBipsLimit; maxFeeBips += 150)
{
for (unsigned int feeBips = 0; feeBips < feeBipsLimit; feeBips += 150)
{
_order.maxFeeBips = maxFeeBips;
_order.feeBips = feeBips;
bool expectedSatisfied = (feeBips <= maxFeeBips);
orderChecked(exchange, _order, expectedSatisfied);
}
}

_order.maxFeeBips = 1002;
_order.feeBips = 1000;
orderChecked(exchange, _order, true);

_order.maxFeeBips = 200;
_order.feeBips = 103;
orderChecked(exchange, _order, false);

_order.maxFeeBips = feeBipsLimit - 1;
_order.feeBips = feeBipsLimit - 1;
orderChecked(exchange, _order, true);

_order.maxFeeBips = feeBipsLimit;
_order.feeBips = feeBipsLimit;
orderChecked(exchange, _order, false);

_order.maxFeeBips = feeBipsLimit - FEE_MULTIPLIER;
_order.feeBips = feeBipsLimit - FEE_MULTIPLIER;
orderChecked(exchange, _order, true);
}
SECTION("tokenS == tokenB")
{
Expand Down
52 changes: 52 additions & 0 deletions packages/loopring_v3/test/testExchangeRingSettlement.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,58 @@ contract("Exchange", (accounts: string[]) => {
await verify();
});

it("High fee trade", async () => {
const ringA: SpotTrade = {
orderA: {
tokenS: "WETH",
tokenB: "GTO",
amountS: new BN(web3.utils.toWei("100", "ether")),
amountB: new BN(web3.utils.toWei("200", "ether")),
maxFeeBips: 100
},
orderB: {
tokenS: "GTO",
tokenB: "WETH",
amountS: new BN(web3.utils.toWei("200", "ether")),
amountB: new BN(web3.utils.toWei("100", "ether")),
maxFeeBips: 30
},
expected: {
orderA: { filledFraction: 1.0, spread: new BN(0) },
orderB: { filledFraction: 1.0 }
}
};
const ringB: SpotTrade = {
orderA: {
tokenS: "WETH",
tokenB: "GTO",
amountS: new BN(web3.utils.toWei("100", "ether")),
amountB: new BN(web3.utils.toWei("200", "ether")),
maxFeeBips: 63 * 50
},
orderB: {
tokenS: "GTO",
tokenB: "WETH",
amountS: new BN(web3.utils.toWei("200", "ether")),
amountB: new BN(web3.utils.toWei("100", "ether")),
maxFeeBips: 63
},
expected: {
orderA: { filledFraction: 1.0, spread: new BN(0) },
orderB: { filledFraction: 1.0 }
}
};

await exchangeTestUtil.setupRing(ringA);
await exchangeTestUtil.setupRing(ringB);
await exchangeTestUtil.sendRing(ringA);
await exchangeTestUtil.sendRing(ringB);

await exchangeTestUtil.submitTransactions();

await verify();
});

it("Matchable (orderA < orderB)", async () => {
const ring: SpotTrade = {
orderA: {
Expand Down
Loading

0 comments on commit 23ffef0

Please sign in to comment.