Skip to content
Open
Show file tree
Hide file tree
Changes from 8 commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
ad8c91b
sdk: release v0.1.18-master.7
github-actions[bot] Jan 28, 2022
b4fcf3e
latest
lmvdz Jan 28, 2022
97dbdcc
Merge branch 'master' of https://github.com/lmvdz/protocol-v1
lmvdz Jan 28, 2022
bbef33b
Merge branch 'drift-labs:master' into master
lmvdz Jan 31, 2022
d30f603
sdk: release v0.1.18-master.8
github-actions[bot] Jan 31, 2022
1d86b5e
Merge branch 'drift-labs:master' into master
lmvdz Feb 1, 2022
9c22500
sdk: release v0.1.18-master.9
github-actions[bot] Feb 1, 2022
f69bad5
fix merge
lmvdz Feb 9, 2022
cf3017a
sdk: release v0.1.21-master.0
github-actions[bot] Feb 9, 2022
63c5a1a
add fees math
lmvdz Feb 15, 2022
c5a7833
fix up imports
lmvdz Feb 15, 2022
739ce25
return discount
lmvdz Feb 15, 2022
be05f54
fix typescript config errors
lmvdz Feb 15, 2022
0aa4578
merge from upstream/crispheaney/off-chain-orders
lmvdz Feb 15, 2022
28cea4d
add fees to exports
lmvdz Feb 15, 2022
6e2cc0b
add return type to calculateOrderFeeTier
lmvdz Feb 15, 2022
221fe0a
convert bn array return types into dev friendly interfaces
lmvdz Feb 15, 2022
f91d3ca
export calculateAmountToTrade
lmvdz Feb 16, 2022
a4a12b6
Merge remote-tracking branch 'upstream/crispheaney/off-chain-orders' …
lmvdz Feb 17, 2022
78455a3
merge from upstream
lmvdz Feb 17, 2022
5712839
clearing_house: init orders
crispheaney Feb 18, 2022
201611a
sdk: release v0.1.21-master.3
github-actions[bot] Feb 18, 2022
62de3d7
sdk: amm.ts add k/repeg budget
0xbigz Feb 18, 2022
9736da1
sdk: fix merge issues from orders
crispheaney Feb 18, 2022
f2d6f30
sdk: fix prettier
crispheaney Feb 18, 2022
0fc9be2
sdk: release v0.1.21-master.4
github-actions[bot] Feb 18, 2022
9efb1d4
clearing_house: support 32 orders
crispheaney Feb 18, 2022
8bfdfaf
sdk: release v0.1.21-master.5
github-actions[bot] Feb 18, 2022
f8c80cf
clearing_house: remove default trait implementations for history structs
crispheaney Feb 19, 2022
4eab8dd
Merge remote-tracking branch 'upstream/crispheaney/off-chain-orders' …
lmvdz Feb 21, 2022
c748c64
clearing_house: fix reduce only
crispheaney Feb 21, 2022
c75ffa2
sdk: release v0.1.22
crispheaney Feb 21, 2022
ae08635
latest
lmvdz Feb 21, 2022
b1c00de
merge master
lmvdz Feb 21, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions sdk/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
"scripts": {
"lint": "eslint './**/*.{ts,tsx}' --quiet",
"build": "yarn clean && tsc",
"build-win": "yarn clean-win && tsc",
"clean-win": "rmdir /s /q lib",
"clean": "rm -rf lib",
"patch-and-pub": "npm version patch --force && npm publish"
},
Expand Down Expand Up @@ -45,5 +47,8 @@
"description": "SDK for Drift Protocol v1",
"engines": {
"node": ">=12"
},
"devDependencies": {
"typescript": "^4.5.5"
}
}
1 change: 1 addition & 0 deletions sdk/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ export * from './math/position';
export * from './math/amm';
export * from './math/trade';
export * from './math/orders';
export * from './math/fees';
export * from './orders';
export * from './orderParams';
export * from './wallet';
Expand Down
148 changes: 148 additions & 0 deletions sdk/src/math/fees.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
import { BN, ClearingHouseUser, FeeStructure, ZERO } from "@drift-labs/sdk";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why do these need to be imported from @drift-labs/sdk? I think they should be imported from same package.

import { AccountInfo } from "@solana/spl-token";
import { OrderDiscountTier, OrderFillerRewardStructure } from "../types";


export interface MarketOrderFee {
userFee: BN,
feeToMarket: BN,
tokenDiscount: BN,
referrerReward: BN,
refereeDiscount: BN
}

export function calculateFeeForMarketOrder(quoteAssetAmount: BN, feeStructure: FeeStructure, discountToken?: AccountInfo, referrer?: ClearingHouseUser ) : MarketOrderFee {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

instead of passing in ClearingHouseUser as referrer, might just be nice to pass in a boolean so you dont need a whole ClearingHouseUser object

const fee = quoteAssetAmount.mul(feeStructure.feeNumerator).div(feeStructure.feeDenominator);
const tokenDiscount = calculateTokenDiscount(fee, feeStructure, discountToken);
const { referrerReward, refereeDiscount } = calculateReferralRewardAndRefereeDiscount(fee, feeStructure, referrer);
const userFee = fee.sub(tokenDiscount).sub(refereeDiscount);
const feeToMarket = userFee.sub(referrerReward);
return { userFee,feeToMarket,tokenDiscount,referrerReward,refereeDiscount } as MarketOrderFee;
}

export function calculateTokenDiscount(fee: BN, feeStructure: FeeStructure, discountToken?: AccountInfo) : BN {
let discount = ZERO;

if (!discountToken) {
return discount;
}

Object.keys(feeStructure.discountTokenTiers).forEach(tier => {
const possibleDiscount = tryToCalculateTokenDiscountForTier(fee, feeStructure.discountTokenTiers[tier], discountToken);
if (possibleDiscount > discount) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i think this needs to be gt instead of > since it is a BN

discount = possibleDiscount;
}
});

return discount;

}

export function tryToCalculateTokenDiscountForTier(fee : BN, tier : { minimumBalance: BN, discountNumerator: BN, discountDenominator: BN }, discountToken : AccountInfo) : BN {
if (belongsToTier(tier, discountToken)) {
return calculateTokenDiscountForTier(fee, tier);
}
return ZERO;
}

export function calculateTokenDiscountForTier(fee : BN, tier : { minimumBalance: BN, discountNumerator: BN, discountDenominator: BN }) : BN {
return fee.mul(tier.discountNumerator).div(tier.discountDenominator);
}

export function belongsToTier(tier: { minimumBalance: BN, discountNumerator: BN, discountDenominator: BN }, discountToken: AccountInfo) : boolean {
return discountToken.amount >= tier.minimumBalance;
}

export interface ReferralRewardDiscount {
referrerReward: BN,
refereeDiscount: BN
}

export function calculateReferralRewardAndRefereeDiscount(fee: BN, feeStructure: FeeStructure, referrer?: ClearingHouseUser) : ReferralRewardDiscount {
let [referrerReward, refereeDiscount] = [ZERO, ZERO];
if (referrer) {
referrerReward = fee.mul(feeStructure.referralDiscount.referrerRewardNumerator).div(feeStructure.referralDiscount.referrerRewardDenominator);
refereeDiscount = fee.mul(feeStructure.referralDiscount.refereeDiscountNumerator).div(feeStructure.referralDiscount.refereeDiscountDenominator);
}
return { referrerReward, refereeDiscount } as ReferralRewardDiscount;
}

export function calculateOrderFeeTier(feeStructure: FeeStructure, discountToken?: AccountInfo) : OrderDiscountTier {

let tier = OrderDiscountTier.NONE;
let tierIndex = -1;
if (discountToken) {
Object.keys(feeStructure.discountTokenTiers).forEach((discountTier, index) => {
if (belongsToTier(feeStructure.discountTokenTiers[discountTier], discountToken) && index > tierIndex) {
tierIndex = index;
tier = OrderDiscountTier[discountTier.split('Tier')[0].toUpperCase()];
}
});
}
return tier;

}

export interface LimitOrderFee {
userFee: BN,
feeToMarket: BN,
tokenDiscount: BN,
fillerReward: BN,
refereeDiscount: BN
}

export function calculateFeeForLimitOrder(quoteAssetAmount : BN, feeStructure: FeeStructure, fillerRewardStructure: OrderFillerRewardStructure, orderFeeTier: OrderDiscountTier, orderTs: BN, now: BN, referrer?: ClearingHouseUser, fillerIsTaker = false) : LimitOrderFee {
const fee = quoteAssetAmount.mul(feeStructure.feeNumerator).div(feeStructure.feeDenominator);
const tokenDiscount = calculateTokenDiscountForLimitOrder(fee, feeStructure, orderFeeTier);
const { referrerReward, refereeDiscount } = calculateReferralRewardAndRefereeDiscount(fee, feeStructure, referrer);
const userFee = fee.sub(refereeDiscount).sub(tokenDiscount);
const fillerReward = fillerIsTaker ? ZERO : calculateFillerReward(userFee, orderTs, now, fillerRewardStructure);
const feeToMarket = userFee.sub(fillerReward).sub(referrerReward);
return { userFee, feeToMarket, tokenDiscount, fillerReward, referrerReward, refereeDiscount } as LimitOrderFee;
}

export function calculateTokenDiscountForLimitOrder(fee: BN, feeStructure: FeeStructure, orderFeeTier: OrderDiscountTier) : BN {
let tokenDiscount = ZERO;
switch(orderFeeTier) {
case OrderDiscountTier.FIRST:
tokenDiscount = calculateTokenDiscountForTier(fee, feeStructure.discountTokenTiers.firstTier);
break;
case OrderDiscountTier.SECOND:
tokenDiscount = calculateTokenDiscountForTier(fee, feeStructure.discountTokenTiers.secondTier);
break;
case OrderDiscountTier.THIRD:
tokenDiscount = calculateTokenDiscountForTier(fee, feeStructure.discountTokenTiers.thirdTier);
break;
case OrderDiscountTier.FOURTH:
tokenDiscount = calculateTokenDiscountForTier(fee, feeStructure.discountTokenTiers.fourthTier);
break;
}
return tokenDiscount;
}

// https://github.com/indutny/bn.js/pull/277
// https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Babylonian_method
function sqrt (input: BN) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let z = new BN(0);
if (input.gt(new BN(3))) {
z = input;
let x = input.div(new BN(2)).add(new BN(1));
while (x.lt(z)) {
z = x;
x = input.div(x).add(x).div(new BN(2));
}
} else if (!input.eq(new BN(0))) {
z = new BN(1);
}
return z;
}

export function calculateFillerReward(userFee: BN, orderTs: BN, now: BN, fillerRewardStructure: OrderFillerRewardStructure) : BN {
// incentivize keepers to prioritize filling older orders (rather than just largest orders)
// for sufficiently small-sized order, reward based on fraction of fee paid
const sizeFillerReward = userFee.mul(fillerRewardStructure.rewardNumerator).div(fillerRewardStructure.rewardDenominator);
const timeSinceOrder = BN.max(new BN(1), now.sub(orderTs));
const timeFillerReward = (sqrt(sqrt(timeSinceOrder.mul(new BN(10 ** 8)))).mul(fillerRewardStructure.timeBasedRewardLowerBound)).div(new BN(100));

return BN.min(sizeFillerReward, timeFillerReward);
}
4 changes: 4 additions & 0 deletions sdk/yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1786,6 +1786,10 @@ tweetnacl@^1.0.0:
resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-1.0.3.tgz#ac0af71680458d8a6378d0d0d050ab1407d35596"
integrity sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==

typescript@^4.5.5:
version "4.5.5"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.5.5.tgz#d8c953832d28924a9e3d37c73d729c846c5896f3"
integrity sha512-TCTIul70LyWe6IJWT8QSYeA54WQe8EjQFU4wY52Fasj5UKx88LNYKCgBEHcOMOrFF1rKGbD8v/xcNWVUq9SymA==
type-check@^0.4.0, type-check@~0.4.0:
version "0.4.0"
resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1"
Expand Down