Skip to content

Commit 23ffef0

Browse files
authored
[protocol 3] Addded option for increased trading fees (#2335)
1 parent 5d8542e commit 23ffef0

File tree

10 files changed

+211
-47
lines changed

10 files changed

+211
-47
lines changed

packages/loopring_v3.js/src/constants.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ export class Constants {
1717

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

20+
static readonly FEE_MULTIPLIER = 50;
21+
2022
static readonly Float24Encoding: FloatEncoding = {
2123
numBitsExponent: 5,
2224
numBitsMantissa: 19,

packages/loopring_v3.js/src/request_processors/spot_trade_processor.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,11 +65,17 @@ export class SpotTradeProcessor {
6565

6666
// Further extraction of packed data
6767
const limitMaskA = orderDataA & 0b10000000;
68-
const feeBipsA = orderDataA & 0b00111111;
68+
const feeBipsMultiplierFlagA = orderDataA & 0b01000000;
69+
const feeBipsA =
70+
(orderDataA & 0b00111111) *
71+
(feeBipsMultiplierFlagA ? Constants.FEE_MULTIPLIER : 1);
6972
const fillAmountBorSA = limitMaskA > 0;
7073

7174
const limitMaskB = orderDataB & 0b10000000;
72-
const feeBipsB = orderDataB & 0b00111111;
75+
const feeBipsMultiplierFlagB = orderDataB & 0b01000000;
76+
const feeBipsB =
77+
(orderDataB & 0b00111111) *
78+
(feeBipsMultiplierFlagB ? Constants.FEE_MULTIPLIER : 1);
7379
const fillAmountBorSB = limitMaskB > 0;
7480

7581
// Decode the float values

packages/loopring_v3/circuit/Circuits/SpotTradeCircuit.h

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -346,11 +346,11 @@ class SpotTradeCircuit : public BaseTransactionCircuit
346346
fillS_B.bits(),
347347

348348
orderA.fillAmountBorS.bits,
349-
VariableArrayT(1, state.constants._0),
350-
orderA.feeBips.bits,
349+
orderA.feeBipsMultiplierFlag.bits,
350+
orderA.feeBipsData.bits,
351351
orderB.fillAmountBorS.bits,
352-
VariableArrayT(1, state.constants._0),
353-
orderB.feeBips.bits,
352+
orderB.feeBipsMultiplierFlag.bits,
353+
orderB.feeBipsData.bits,
354354
});
355355
}
356356
};

packages/loopring_v3/circuit/Gadgets/MathGadgets.h

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,7 @@ namespace Loopring
2121

2222
// All Poseidon permutations used
2323
using Poseidon_2 = Poseidon_gadget_T<3, 1, 6, 51, 2, 1>;
24-
template <unsigned n_outputs>
25-
using Poseidon_4_ = Poseidon_gadget_T<5, 1, 6, 52, n_outputs, 1>;
24+
template <unsigned n_outputs> using Poseidon_4_ = Poseidon_gadget_T<5, 1, 6, 52, n_outputs, 1>;
2625
using Poseidon_4 = Poseidon_4_<4>;
2726
using Poseidon_5 = Poseidon_gadget_T<6, 1, 6, 52, 5, 1>;
2827
using Poseidon_6 = Poseidon_gadget_T<7, 1, 6, 52, 6, 1>;
@@ -70,6 +69,7 @@ class Constants : public GadgetT
7069
const VariableT txTypeSpotTrade;
7170
const VariableT txTypeTransfer;
7271
const VariableT txTypeWithdrawal;
72+
const VariableT feeMultiplier;
7373

7474
const VariableArrayT zeroAccount;
7575

@@ -105,6 +105,7 @@ class Constants : public GadgetT
105105
make_variable(pb, ethsnarks::FieldT(int(TransactionType::Transfer)), FMT(prefix, ".txTypeTransfer"))),
106106
txTypeWithdrawal(
107107
make_variable(pb, ethsnarks::FieldT(int(TransactionType::Withdrawal)), FMT(prefix, ".txTypeWithdrawal"))),
108+
feeMultiplier(make_variable(pb, ethsnarks::FieldT(FEE_MULTIPLIER), FMT(prefix, ".feeMultiplier"))),
108109

109110
zeroAccount(NUM_BITS_ACCOUNT, _0)
110111
{
@@ -161,13 +162,14 @@ class Constants : public GadgetT
161162
pb.add_r1cs_constraint(
162163
ConstraintT(txTypeWithdrawal, FieldT::one(), ethsnarks::FieldT(int(TransactionType::Withdrawal))),
163164
".txTypeWithdrawal");
165+
pb.add_r1cs_constraint(
166+
ConstraintT(feeMultiplier, FieldT::one(), ethsnarks::FieldT(FEE_MULTIPLIER)), ".feeMultiplier");
164167
}
165168
};
166169

167170
class DualVariableGadget : public libsnark::dual_variable_gadget<FieldT>
168171
{
169172
public:
170-
171173
DualVariableGadget( //
172174
ProtoboardT &pb,
173175
const size_t width,
@@ -205,15 +207,13 @@ class DualVariableGadget : public libsnark::dual_variable_gadget<FieldT>
205207
class ToBitsGadget : public libsnark::dual_variable_gadget<FieldT>
206208
{
207209
public:
208-
209210
ToBitsGadget( //
210211
ProtoboardT &pb,
211212
const VariableT &value,
212213
const size_t width,
213214
const std::string &prefix)
214215
: libsnark::dual_variable_gadget<FieldT>(pb, value, width, prefix)
215216
{
216-
217217
}
218218

219219
void generate_r1cs_witness()
@@ -232,14 +232,12 @@ typedef ToBitsGadget RangeCheckGadget;
232232
class FromBitsGadget : public libsnark::dual_variable_gadget<FieldT>
233233
{
234234
public:
235-
236235
FromBitsGadget( //
237236
ProtoboardT &pb,
238237
const VariableArrayT &bits,
239238
const std::string &prefix)
240239
: libsnark::dual_variable_gadget<FieldT>(pb, bits, prefix)
241240
{
242-
243241
}
244242

245243
void generate_r1cs_witness()

packages/loopring_v3/circuit/Gadgets/OrderGadgets.h

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,13 +35,20 @@ class OrderGadget : public GadgetT
3535
DualVariableGadget feeBips;
3636
DualVariableGadget amm;
3737

38+
DualVariableGadget feeBipsMultiplierFlag;
39+
DualVariableGadget feeBipsData;
40+
3841
NotGadget notAmm;
3942

4043
// Checks
4144
RequireLeqGadget feeBips_leq_maxFeeBips;
4245
RequireNotEqualGadget tokenS_neq_tokenB;
4346
RequireNotZeroGadget amountS_notZero;
4447
RequireNotZeroGadget amountB_notZero;
48+
// Fee checks
49+
TernaryGadget feeMultiplier;
50+
UnsafeMulGadget decodedFeeBips;
51+
RequireEqualGadget feeBipsEqualsDecodedFeeBips;
4552

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

78+
feeBipsMultiplierFlag(pb, 1, FMT(prefix, ".feeBipsMultiplierFlag")),
79+
feeBipsData(pb, NUM_BITS_BIPS_DA, FMT(prefix, ".feeBipsData")),
80+
7181
notAmm(pb, amm.packed, FMT(prefix, ".notAmm")),
7282

7383
// Checks
@@ -80,6 +90,19 @@ class OrderGadget : public GadgetT
8090
tokenS_neq_tokenB(pb, tokenS.packed, tokenB.packed, FMT(prefix, ".tokenS != tokenB")),
8191
amountS_notZero(pb, amountS.packed, FMT(prefix, ".amountS != 0")),
8292
amountB_notZero(pb, amountB.packed, FMT(prefix, ".amountB != 0")),
93+
// Fee checks
94+
feeMultiplier(
95+
pb,
96+
feeBipsMultiplierFlag.packed,
97+
constants.feeMultiplier,
98+
constants._1,
99+
FMT(prefix, ".feeMultiplier")),
100+
decodedFeeBips(pb, feeBipsData.packed, feeMultiplier.result(), FMT(prefix, ".decodedFeeBips")),
101+
feeBipsEqualsDecodedFeeBips(
102+
pb,
103+
feeBips.packed,
104+
decodedFeeBips.result(),
105+
FMT(prefix, ".feeBipsEqualsDecodedFeeBips")),
83106

84107
// Signature
85108
hash(
@@ -117,13 +140,30 @@ class OrderGadget : public GadgetT
117140
feeBips.generate_r1cs_witness(pb, order.feeBips);
118141
amm.generate_r1cs_witness(pb, order.amm);
119142

143+
// Use the fee multiplier if necessary
144+
if (toBigInt(order.feeBips) >= 64 /*2**NUM_BITS_BIPS_DA*/)
145+
{
146+
feeBipsMultiplierFlag.generate_r1cs_witness(pb, ethsnarks::FieldT(1));
147+
feeBipsData.generate_r1cs_witness(
148+
pb, ethsnarks::FieldT((toBigInt(order.feeBips) / FEE_MULTIPLIER).to_int()));
149+
}
150+
else
151+
{
152+
feeBipsMultiplierFlag.generate_r1cs_witness(pb, ethsnarks::FieldT(0));
153+
feeBipsData.generate_r1cs_witness(pb, order.feeBips);
154+
}
155+
120156
notAmm.generate_r1cs_witness();
121157

122158
// Checks
123159
feeBips_leq_maxFeeBips.generate_r1cs_witness();
124160
tokenS_neq_tokenB.generate_r1cs_witness();
125161
amountS_notZero.generate_r1cs_witness();
126162
amountB_notZero.generate_r1cs_witness();
163+
// Fee checks
164+
feeMultiplier.generate_r1cs_witness();
165+
decodedFeeBips.generate_r1cs_witness();
166+
feeBipsEqualsDecodedFeeBips.generate_r1cs_witness();
127167

128168
// Signature
129169
hash.generate_r1cs_witness();
@@ -145,13 +185,20 @@ class OrderGadget : public GadgetT
145185
feeBips.generate_r1cs_constraints(true);
146186
amm.generate_r1cs_constraints(true);
147187

188+
feeBipsMultiplierFlag.generate_r1cs_constraints(true);
189+
feeBipsData.generate_r1cs_constraints(true);
190+
148191
notAmm.generate_r1cs_constraints();
149192

150193
// Checks
151194
feeBips_leq_maxFeeBips.generate_r1cs_constraints();
152195
tokenS_neq_tokenB.generate_r1cs_constraints();
153196
amountS_notZero.generate_r1cs_constraints();
154197
amountB_notZero.generate_r1cs_constraints();
198+
// Fee checks
199+
feeMultiplier.generate_r1cs_constraints();
200+
decodedFeeBips.generate_r1cs_constraints();
201+
feeBipsEqualsDecodedFeeBips.generate_r1cs_constraints();
155202

156203
// Signature
157204
hash.generate_r1cs_constraints();

packages/loopring_v3/circuit/Gadgets/SignatureGadgets.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -372,7 +372,8 @@ class SignatureVerifier : public GadgetT
372372
{
373373
for (unsigned int i = 0; i < sig_s.size(); i++)
374374
{
375-
libsnark::generate_boolean_r1cs_constraint<ethsnarks::FieldT>(pb, sig_s[i], FMT(annotation_prefix, ".bitness"));
375+
libsnark::generate_boolean_r1cs_constraint<ethsnarks::FieldT>(
376+
pb, sig_s[i], FMT(annotation_prefix, ".bitness"));
376377
}
377378
signatureVerifier.generate_r1cs_constraints();
378379
valid.generate_r1cs_constraints();

packages/loopring_v3/circuit/Utils/Constants.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@ static const unsigned int NUM_BITS_TOKEN = TREE_DEPTH_TOKENS * 2;
2020
static const unsigned int NUM_BITS_STORAGEID = 32;
2121
static const unsigned int NUM_BITS_TIMESTAMP = 32;
2222
static const unsigned int NUM_BITS_NONCE = 32;
23-
static const unsigned int NUM_BITS_BIPS = 6;
23+
static const unsigned int NUM_BITS_BIPS = 12; // ceil(log2(2**NUM_BITS_BIPS_DA * FEE_MULTIPLIER))
24+
static const unsigned int NUM_BITS_BIPS_DA = 6;
2425
static const unsigned int NUM_BITS_PROTOCOL_FEE_BIPS = 8;
2526
static const unsigned int NUM_BITS_TYPE = 8;
2627
static const unsigned int NUM_STORAGE_SLOTS = 16384; // 2**NUM_BITS_STORAGE_ADDRESS
@@ -35,6 +36,7 @@ static const char *EMPTY_TRADE_HISTORY = "65927491675782344981534105642433692294
3536
static const char *MAX_AMOUNT = "79228162514264337593543950335"; // 2^96 - 1
3637
static const char *FIXED_BASE = "1000000000000000000"; // 10^18
3738
static const unsigned int NUM_BITS_FIXED_BASE = 60; // ceil(log2(10^18))
39+
static const unsigned int FEE_MULTIPLIER = 50;
3840

3941
struct FloatEncoding
4042
{

packages/loopring_v3/circuit/test/OrderTests.cpp

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -101,16 +101,47 @@ TEST_CASE("Order", "[OrderGadget]")
101101
SECTION("feeBips > maxFeeBips")
102102
{
103103
Order _order = order;
104-
for (unsigned int maxFeeBips = 0; maxFeeBips < pow(2, NUM_BITS_BIPS); maxFeeBips += 3)
104+
for (unsigned int maxFeeBips = 0; maxFeeBips < pow(2, NUM_BITS_BIPS_DA); maxFeeBips += 3)
105105
{
106-
for (unsigned int feeBips = 0; feeBips < pow(2, NUM_BITS_BIPS); feeBips += 3)
106+
for (unsigned int feeBips = 0; feeBips < pow(2, NUM_BITS_BIPS_DA); feeBips += 3)
107107
{
108108
_order.maxFeeBips = maxFeeBips;
109109
_order.feeBips = feeBips;
110110
bool expectedSatisfied = (feeBips <= maxFeeBips);
111111
orderChecked(exchange, _order, expectedSatisfied);
112112
}
113113
}
114+
unsigned int feeBipsLimit = pow(2, NUM_BITS_BIPS_DA) * FEE_MULTIPLIER;
115+
for (unsigned int maxFeeBips = 0; maxFeeBips < feeBipsLimit; maxFeeBips += 150)
116+
{
117+
for (unsigned int feeBips = 0; feeBips < feeBipsLimit; feeBips += 150)
118+
{
119+
_order.maxFeeBips = maxFeeBips;
120+
_order.feeBips = feeBips;
121+
bool expectedSatisfied = (feeBips <= maxFeeBips);
122+
orderChecked(exchange, _order, expectedSatisfied);
123+
}
124+
}
125+
126+
_order.maxFeeBips = 1002;
127+
_order.feeBips = 1000;
128+
orderChecked(exchange, _order, true);
129+
130+
_order.maxFeeBips = 200;
131+
_order.feeBips = 103;
132+
orderChecked(exchange, _order, false);
133+
134+
_order.maxFeeBips = feeBipsLimit - 1;
135+
_order.feeBips = feeBipsLimit - 1;
136+
orderChecked(exchange, _order, true);
137+
138+
_order.maxFeeBips = feeBipsLimit;
139+
_order.feeBips = feeBipsLimit;
140+
orderChecked(exchange, _order, false);
141+
142+
_order.maxFeeBips = feeBipsLimit - FEE_MULTIPLIER;
143+
_order.feeBips = feeBipsLimit - FEE_MULTIPLIER;
144+
orderChecked(exchange, _order, true);
114145
}
115146
SECTION("tokenS == tokenB")
116147
{

packages/loopring_v3/test/testExchangeRingSettlement.ts

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,58 @@ contract("Exchange", (accounts: string[]) => {
6969
await verify();
7070
});
7171

72+
it("High fee trade", async () => {
73+
const ringA: SpotTrade = {
74+
orderA: {
75+
tokenS: "WETH",
76+
tokenB: "GTO",
77+
amountS: new BN(web3.utils.toWei("100", "ether")),
78+
amountB: new BN(web3.utils.toWei("200", "ether")),
79+
maxFeeBips: 100
80+
},
81+
orderB: {
82+
tokenS: "GTO",
83+
tokenB: "WETH",
84+
amountS: new BN(web3.utils.toWei("200", "ether")),
85+
amountB: new BN(web3.utils.toWei("100", "ether")),
86+
maxFeeBips: 30
87+
},
88+
expected: {
89+
orderA: { filledFraction: 1.0, spread: new BN(0) },
90+
orderB: { filledFraction: 1.0 }
91+
}
92+
};
93+
const ringB: SpotTrade = {
94+
orderA: {
95+
tokenS: "WETH",
96+
tokenB: "GTO",
97+
amountS: new BN(web3.utils.toWei("100", "ether")),
98+
amountB: new BN(web3.utils.toWei("200", "ether")),
99+
maxFeeBips: 63 * 50
100+
},
101+
orderB: {
102+
tokenS: "GTO",
103+
tokenB: "WETH",
104+
amountS: new BN(web3.utils.toWei("200", "ether")),
105+
amountB: new BN(web3.utils.toWei("100", "ether")),
106+
maxFeeBips: 63
107+
},
108+
expected: {
109+
orderA: { filledFraction: 1.0, spread: new BN(0) },
110+
orderB: { filledFraction: 1.0 }
111+
}
112+
};
113+
114+
await exchangeTestUtil.setupRing(ringA);
115+
await exchangeTestUtil.setupRing(ringB);
116+
await exchangeTestUtil.sendRing(ringA);
117+
await exchangeTestUtil.sendRing(ringB);
118+
119+
await exchangeTestUtil.submitTransactions();
120+
121+
await verify();
122+
});
123+
72124
it("Matchable (orderA < orderB)", async () => {
73125
const ring: SpotTrade = {
74126
orderA: {

0 commit comments

Comments
 (0)