UNPKG

@gnosis.pm/dex-contracts

Version:

Contracts for dFusion multi-token batch auction exchange

660 lines (659 loc) 20 kB
import BN from "bn.js"; import { toETH, feeAdded, feeSubtracted, ERROR_EPSILON } from "../math"; import { generateTestCase } from "./generate"; export * from "./generate"; const ZERO = new BN(0); export const basicTrade = generateTestCase({ name: "Basic Trade", orders: [ { sellToken: 0, buyToken: 1, sellAmount: feeAdded(toETH(20)).add(ERROR_EPSILON), buyAmount: toETH(10), user: 0, }, { sellToken: 1, buyToken: 0, sellAmount: toETH(10), buyAmount: feeSubtracted(toETH(20)).sub(ERROR_EPSILON), user: 1, }, ], solutions: [ { name: "Full Solution", prices: [1, 2].map(toETH), buyVolumes: [toETH(10), feeSubtracted(toETH(20))], }, { name: "Partial Solution", prices: [1, 2].map(toETH), buyVolumes: [toETH(9), feeSubtracted(toETH(18))], }, ], }); export const advancedTrade = generateTestCase({ name: "Advanced Trade", orders: [ { sellToken: 0, buyToken: 1, sellAmount: feeAdded(toETH(20)).add(ERROR_EPSILON), buyAmount: toETH(10), user: 0, }, { sellToken: 1, buyToken: 0, sellAmount: toETH(10), buyAmount: feeSubtracted(toETH(20)).sub(ERROR_EPSILON), user: 1, }, { sellToken: 0, buyToken: 1, sellAmount: feeAdded(toETH(20)).add(ERROR_EPSILON), buyAmount: toETH(10), user: 2, }, { sellToken: 1, buyToken: 0, sellAmount: toETH(10), buyAmount: feeSubtracted(toETH(20)).sub(ERROR_EPSILON), user: 3, }, { sellToken: 0, buyToken: 1, sellAmount: feeAdded(toETH(20)).add(ERROR_EPSILON), buyAmount: toETH(10), user: 4, }, { sellToken: 1, buyToken: 0, sellAmount: toETH(10), buyAmount: feeSubtracted(toETH(20)).sub(ERROR_EPSILON), user: 5, }, ], solutions: [ { name: "Match 1 Pair", prices: [1, 2].map(toETH), buyVolumes: [toETH(10), feeSubtracted(toETH(20)), ZERO, ZERO, ZERO, ZERO], }, { name: "Match 2 Pairs", prices: [1, 2].map(toETH), buyVolumes: [ toETH(10), feeSubtracted(toETH(20)), toETH(10), feeSubtracted(toETH(20)), ZERO, ZERO, ], }, { name: "Match 3 Pairs", prices: [1, 2].map(toETH), buyVolumes: [ toETH(10), feeSubtracted(toETH(20)), toETH(10), feeSubtracted(toETH(20)), toETH(10), feeSubtracted(toETH(20)), ], }, ], }); export const biggieSmallTrade = generateTestCase({ name: "Biggie Small", orders: [ { sellToken: 0, buyToken: 1, sellAmount: toETH(185), buyAmount: toETH(1), user: 0, }, { sellToken: 1, buyToken: 0, sellAmount: toETH(1000), buyAmount: toETH(184000), user: 1, }, ], solutions: [ { name: "Max Fulfillment", prices: [toETH(1), feeAdded(toETH(184))], buyVolumes: [1, 184].map(toETH), }, ], }); export const basicRingTrade = generateTestCase({ name: "Basic Ring", orders: [ { sellToken: 0, buyToken: 1, sellAmount: toETH(1), buyAmount: toETH(0.99), user: 0, }, { sellToken: 1, buyToken: 2, sellAmount: toETH(1), buyAmount: toETH(0.99), user: 1, }, { sellToken: 2, buyToken: 0, sellAmount: toETH(1), buyAmount: toETH(0.99), user: 2, }, ], solutions: [ { name: "Ring Around the Rosie", prices: [1, 1, 1].map(toETH), buyVolumes: [ feeSubtracted(toETH(1), 1), feeSubtracted(toETH(1), 2), feeSubtracted(toETH(1), 3), ], }, ], }); const n = 30; export const largeRing30 = generateTestCase({ name: "Longest Ring Trade", orders: Array.from(Array(n).keys()) .map((i) => ({ sellToken: i % n, buyToken: (i + 1) % n, sellAmount: toETH(1), buyAmount: toETH(0.99), user: i % 2, })) .concat(Array.from(Array(n).keys()).map((i) => ({ sellToken: i % n, buyToken: (i + 1) % n, sellAmount: toETH(1), buyAmount: toETH(0.99), user: 2 + (i % 2), }))), solutions: [ { name: "Solution filling ~90% of the available volume", prices: [ new BN("1000000000000000000"), new BN("999000000079933840"), new BN("998001000078845135"), new BN("997002999140535506"), new BN("996005996035392883"), new BN("995009990083786472"), new BN("994014980130495716"), new BN("993020965086898658"), new BN("992027944164247585"), new BN("991035916140546385"), new BN("990044880290088817"), new BN("989054835391299816"), new BN("988065780510318410"), new BN("987077714823955022"), new BN("986090637068828536"), new BN("985104546427501883"), new BN("984119441847563266"), new BN("983135322401663687"), new BN("982152187132755565"), new BN("981170034910294548"), new BN("980188864899078269"), new BN("979208676045659066"), new BN("978229467305820489"), new BN("977251237858427284"), new BN("976273986614832057"), new BN("975297712672815304"), new BN("974322414953157214"), new BN("973348092529574952"), new BN("982063340537758779"), new BN("990991090090097194"), ], buyVolumes: Array(30) .fill(new BN(0)) .concat([ new BN("899999909927988434"), new BN("899999909928898147"), new BN("899999909873138748"), new BN("899999909968923181"), new BN("899999909928736570"), new BN("899999909895423467"), new BN("899999909952944811"), new BN("899999909914445655"), new BN("899999909986676388"), new BN("899999909926967303"), new BN("899999909943800566"), new BN("899999909985327246"), new BN("899999909899485749"), new BN("899999909936269665"), new BN("899999909940159650"), new BN("899999909970806338"), new BN("899999909974515710"), new BN("899999909925496645"), new BN("899999909957902274"), new BN("899999909936146659"), new BN("899999909925595398"), new BN("899999909984286761"), new BN("899999909965948230"), new BN("899999909971236827"), new BN("899999909930081941"), new BN("899999909936534371"), new BN("899999909944513216"), new BN("891120914814366045"), new BN("882209793797187023"), new BN("873387783197991000"), ]), }, { name: "Full solution to large ring trade", prices: [ new BN("1000000000000000000"), new BN("999000000080932527"), new BN("998001000163952351"), new BN("997002999153048099"), new BN("996005996120621788"), new BN("995009990197501127"), new BN("994014980201782351"), new BN("993020965255715327"), new BN("992027944249700231"), new BN("991035916321243854"), new BN("990044880335294951"), new BN("989054835476903275"), new BN("988065780622280566"), new BN("987077714922662489"), new BN("986090637193173669"), new BN("985104546487832659"), new BN("984119441958561945"), new BN("983135322499977058"), new BN("982152187194035945"), new BN("981170035069292740"), new BN("980188864985054793"), new BN("979208676167400239"), new BN("978229467438980960"), new BN("977251237967802060"), new BN("976273986723981212"), new BN("975297712793135196"), new BN("974322415061859070"), new BN("973348092626909555"), new BN("982063340537758779"), new BN("990991090090097095"), ], buyVolumes: [ new BN("999999900018987366"), new BN("999999899935720167"), new BN("999999899946492752"), new BN("999999899979899439"), new BN("999999899906533389"), new BN("999999899912087908"), new BN("999999899877713252"), new BN("999999899918800176"), new BN("999999899902864002"), new BN("999999899973191776"), new BN("999999899951005325"), new BN("999999899970382379"), new BN("999999899888317721"), new BN("999999899903089341"), new BN("999999899972267613"), new BN("999999899954772667"), new BN("999999899971684198"), new BN("999999899954824426"), new BN("999999899891175087"), new BN("999999899941337511"), new BN("999999899893002057"), new BN("999999899946416798"), new BN("999999899950243776"), new BN("999999899956239066"), new BN("999999899898945833"), new BN("999999899917915929"), new BN("999999899938348141"), new BN("990134349892753397"), new BN("980233104317120013"), new BN("970430870317033000"), ].concat(Array(30).fill(new BN(0))), }, ], }); export const shortRingBetterTrade = generateTestCase({ orders: [ // ring trade orders { sellToken: 0, buyToken: 1, sellAmount: toETH(1), buyAmount: toETH(0.99), user: 0, }, { sellToken: 1, buyToken: 2, sellAmount: toETH(1), buyAmount: toETH(0.99), user: 1, }, { sellToken: 2, buyToken: 0, sellAmount: toETH(1), buyAmount: toETH(0.99), user: 2, }, // biggie small orders { sellToken: 0, buyToken: 1, sellAmount: toETH(185), buyAmount: toETH(1), user: 3, }, { sellToken: 1, buyToken: 0, sellAmount: toETH(1000), buyAmount: toETH(184000), user: 4, }, ], solutions: [ { name: "Ring Trade Solution", prices: [1, 1, 1].map(toETH), buyVolumes: [ feeSubtracted(toETH(1), 1), feeSubtracted(toETH(1), 2), feeSubtracted(toETH(1), 3), ZERO, ZERO, ], }, { name: "Biggie Small Trade", prices: [toETH(1), feeAdded(toETH(184)), ZERO], buyVolumes: [ZERO, ZERO, ZERO, toETH(1), toETH(184)], }, ], }); const maxUint128 = new BN(2).pow(new BN(128)).sub(new BN(1)); export const exampleOrderWithUnlimitedAmount = generateTestCase({ deposits: [ { amount: feeAdded(toETH(10)), token: 0, user: 0 }, { amount: feeAdded(toETH(10)), token: 1, user: 1 }, ], orders: [ { sellToken: 0, buyToken: 1, sellAmount: maxUint128, buyAmount: feeSubtracted(maxUint128.div(new BN(4))), user: 0, }, { sellToken: 1, buyToken: 0, sellAmount: toETH(10), buyAmount: feeSubtracted(toETH(5)), user: 1, }, { sellToken: 0, buyToken: 1, sellAmount: feeAdded(maxUint128.div(new BN(2))), buyAmount: maxUint128, user: 0, }, ], solutions: [ { prices: [1, 0.5].map(toETH), buyVolumes: [ZERO, feeSubtracted(toETH(5)), toETH(10)], }, { prices: [1, 1].map(toETH), buyVolumes: [toETH(10), feeSubtracted(toETH(10)), ZERO], }, ], }, false, false); const fiveThousand = new BN("5000"); const tenThousand = new BN("10000"); export const tooSmallSellAmountTrade = generateTestCase({ deposits: [ { amount: feeAdded(tenThousand), token: 0, user: 0 }, { amount: feeAdded(tenThousand), token: 1, user: 1 }, ], orders: [ { sellToken: 0, buyToken: 1, sellAmount: feeAdded(tenThousand), buyAmount: fiveThousand, user: 0, }, { sellToken: 1, buyToken: 0, sellAmount: feeAdded(tenThousand), buyAmount: fiveThousand, user: 1, }, ], solutions: [ { name: "Small sell amount", prices: [1, 0.9].map(toETH), buyVolumes: [tenThousand, tenThousand], }, ], }, false, true); export const tooSmallBuyAmountTrade = generateTestCase({ deposits: [ { amount: feeAdded(tenThousand), token: 0, user: 0 }, { amount: feeAdded(tenThousand), token: 1, user: 1 }, ], orders: [ { sellToken: 0, buyToken: 1, sellAmount: feeAdded(tenThousand), buyAmount: fiveThousand, user: 0, }, { sellToken: 1, buyToken: 0, sellAmount: feeAdded(tenThousand), buyAmount: fiveThousand, user: 1, }, ], solutions: [ { name: "Small buy amounts", prices: [1, 1].map(toETH), buyVolumes: [10000, 9990].map((val) => new BN(val)), }, ], }, false, true); const fiftyThousand = new BN("50000"); const hundredThousand = new BN("100000"); export const smallExample = generateTestCase({ deposits: [ { amount: feeAdded(hundredThousand), token: 0, user: 0 }, { amount: new BN(190), token: 1, user: 1 }, { amount: new BN(9), token: 0, user: 1 }, { amount: feeAdded(hundredThousand), token: 1, user: 2 }, ], orders: [ { sellToken: 0, buyToken: 1, sellAmount: feeAdded(hundredThousand), buyAmount: fiftyThousand, user: 0, }, { sellToken: 1, buyToken: 0, sellAmount: feeAdded(hundredThousand), buyAmount: fiftyThousand, user: 1, }, { sellToken: 0, buyToken: 1, sellAmount: feeAdded(hundredThousand), buyAmount: fiftyThousand, user: 1, }, { sellToken: 1, buyToken: 0, sellAmount: feeAdded(hundredThousand), buyAmount: fiftyThousand, user: 2, }, ], solutions: [ { name: "Small Solution", prices: [1, 1].map(toETH), buyVolumes: [100000, 99900, 99810, 99711].map((val) => new BN(val)), }, ], }); export const stableXExample = generateTestCase({ deposits: [ { amount: toETH(3000), token: 0, user: 0 }, { amount: toETH(3000), token: 1, user: 0 }, ], orders: [ { sellToken: 0, buyToken: 1, sellAmount: toETH(2000), buyAmount: toETH(999), user: 0, }, { sellToken: 1, buyToken: 0, sellAmount: toETH(999), buyAmount: toETH(1996), user: 1, }, ], solutions: [ { name: "Naive Solver", prices: [toETH(1), new BN("1999998997996995993")], buyVolumes: [toETH(999), new BN("1996000999999999998010")], }, ], }); export const marginalTrade = generateTestCase({ name: "Marginal Trade", orders: [ { sellToken: 0, buyToken: 1, sellAmount: feeAdded(toETH(20)).add(ERROR_EPSILON), buyAmount: toETH(10), user: 0, }, { sellToken: 1, buyToken: 0, sellAmount: toETH(10), buyAmount: feeSubtracted(toETH(20)).sub(ERROR_EPSILON), user: 1, }, { sellToken: 0, buyToken: 1, sellAmount: feeAdded(toETH(200000)).add(ERROR_EPSILON), buyAmount: toETH(100000), user: 2, }, { sellToken: 1, buyToken: 0, sellAmount: toETH(100000), buyAmount: feeSubtracted(toETH(200000)).sub(ERROR_EPSILON), user: 3, }, ], solutions: [ { name: "First Solution", prices: [1, 2].map(toETH), buyVolumes: [toETH(10), feeSubtracted(toETH(20)), ZERO, ZERO], }, { name: "Marginally Solution", prices: [1, 2].map(toETH), buyVolumes: [ZERO, ZERO, toETH(100000), feeSubtracted(toETH(200000))], }, ], }); export const utilityOverflow = generateTestCase({ deposits: [ { amount: toETH(10), token: 0, user: 0 }, { amount: toETH(1), token: 1, user: 1 }, { amount: toETH(100), token: 2, user: 2 }, ], orders: [ { sellToken: 0, buyToken: 1, sellAmount: toETH(10), buyAmount: new BN("10000000000000000"), user: 0, }, { sellToken: 1, buyToken: 2, sellAmount: toETH(1), buyAmount: new BN("1000"), user: 1, }, { sellToken: 2, buyToken: 1, sellAmount: toETH(100), buyAmount: toETH(1), user: 2, }, ], solutions: [ { name: "Utility Overflow", prices: [ "1000000000000000000", "998999900119977150048", "10000000000198528574", ].map((val) => new BN(val)), buyVolumes: [ "1998999800099984", "99800080039994614733", "998000900199892164", ].map((val) => new BN(val)), }, ], });