Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
214 changes: 212 additions & 2 deletions src/main/scala/ContractTesting.scala
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,9 @@ object ContractTesting {
println(s"The contract's FreeMint address is: ${dexyAddresses.freeMintAddress.toString}")
println(s"The contract's ArbitrageMint address is: ${dexyAddresses.arbitrageMintAddress.toString}")
freeMint(ctx)
freeMintCounterReset(ctx)
arbitrageMint(ctx)
arbitrageMintCounterReset(ctx)
})
}

Expand Down Expand Up @@ -159,6 +161,107 @@ object ContractTesting {
println("---- FreeMint scenario Done!! ----")
}

def freeMintCounterReset(ctx: BlockchainContext): Unit = {
println("---- Start FreeMint with CounterReset scenario !! ----")
val ORACLE_ERG_USD_PRICE = 584663187L
val RESET_HEIGHT: Int = 100
val REMAINING_DEXY_USD: Long = 1e15.toLong
val WANT_DEXY = 1000L
val feeNum = 10
val feeDenom = 1000
val NEEDED_ERG = WANT_DEXY * ORACLE_ERG_USD_PRICE * (feeNum + feeDenom) / feeDenom
val DEXY_RESERVES = 185080L
val USER_FOUND: Long = 4000e9.toLong
val BASE_FREE_MINT_VALUE: Long = 2e9.toLong
val BASE_LP_BOX_VALUE: Long = 110806126325195L
val DEXY_FEE: Long = 1e9.toLong // TODO: This value is just for testing purpose and isn't true
val t_free: Int = 100
// dummy prover with dummy secret
val prover = ctx.newProverBuilder()
.withDLogSecret(BigInt.apply(0).bigInteger)
.build()

val tb = ctx.newTxBuilder()
val lpBox = tb.outBoxBuilder()
.contract(new ErgoTreeContract(dexyAddresses.lpAddress.script))
.value(BASE_LP_BOX_VALUE)
.tokens(DexyTokenObj.lpNFT, DexyTokenObj.LPToken, new ErgoToken(DexyToken.dexyUSDToken, DEXY_RESERVES))
.build()
.convertToInputWith("adfd114c8b145dd758248c9dadf818927da3a7cd56bbe4129ba053d1ce0cbc30", 0)

val oracleBox = tb.outBoxBuilder()
.contract(new ErgoTreeContract(Address.create("4MQyML64GnzMxZgm").getErgoAddress.script))
.value(2e9.toLong)
.tokens(DexyTokenObj.oracleNFT)
.registers(ErgoValue.of(ORACLE_ERG_USD_PRICE))
.build()
.convertToInputWith("96be1e2f79bfb4ef587b4480966efe9e90f0dddd89c616a1f65222ea2ac4e351", 0)

val bankBox = tb.outBoxBuilder()
.contract(new ErgoTreeContract(dexyAddresses.bankAddress.script))
.value((11610).toLong)
.tokens(DexyTokenObj.bankNFT, DexyTokenObj.dexyUSDToken)
.build()
.convertToInputWith("ce552663312afc2379a91f803c93e2b10b424f176fbc930055c10def2fd88a5d", 0)

val freeMintBox = tb.outBoxBuilder()
.contract(new ErgoTreeContract(dexyAddresses.freeMintAddress.script))
.value(BASE_FREE_MINT_VALUE)
.tokens(DexyTokenObj.freeMintNFT)
.registers(ErgoValue.of(RESET_HEIGHT), ErgoValue.of(REMAINING_DEXY_USD))
.build()
.convertToInputWith("55c0537aafa6932c9a25c856fcd064d317af96a4fa733012c26d836bbaf5689a", 1)

val userInBox = tb.outBoxBuilder()
.contract(new ErgoTreeContract(Address.create("4MQyML64GnzMxZgm").getErgoAddress.script))
.value(USER_FOUND)
.build()
.convertToInputWith("b84e555e05181c1c8cf661535bb197853fe4ec2299f2fd399ba7bb1e73f5e69b", 0)

val newBankBox = tb.outBoxBuilder()
.contract(new ErgoTreeContract(dexyAddresses.bankAddress.script))
.value(bankBox.getValue + NEEDED_ERG)
.tokens(DexyTokenObj.bankNFT, new ErgoToken(DexyToken.dexyUSDToken, bankBox.getTokens.get(1).getValue - WANT_DEXY))
.build()

val newFreeMintBox = tb.outBoxBuilder()
.contract(new ErgoTreeContract(dexyAddresses.freeMintAddress.script))
.value(BASE_FREE_MINT_VALUE + DEXY_FEE)
.tokens(DexyTokenObj.freeMintNFT)
.registers(ErgoValue.of(ctx.getHeight + t_free), ErgoValue.of(DEXY_RESERVES / 100 - WANT_DEXY))
.build()

val userOutBox = tb.outBoxBuilder()
.contract(new ErgoTreeContract(Address.create("4MQyML64GnzMxZgm").getErgoAddress.script))
.value(USER_FOUND - NEEDED_ERG - FEE - DEXY_FEE)
.tokens(new ErgoToken(DexyToken.dexyUSDToken, WANT_DEXY))
.build()

val tx = tb.boxesToSpend(Seq(
freeMintBox,
bankBox,
userInBox
).asJava)
.fee(FEE)
.outputs(newFreeMintBox, newBankBox, userOutBox)
.withDataInputs(List(oracleBox, lpBox).asJava)
.sendChangeTo(Address.create("4MQyML64GnzMxZgm").getErgoAddress)
.build()

var signed: SignedTransaction = null
try {
signed = prover.sign(tx)
} catch {
case e: Exception => {
println("User could not sing tx due to:")
println(e)
return
}
}
println(s"signed tx: ${signed.toJson(false)}")
println("---- FreeMint scenario with CounterReset Done!! ----")
}

def arbitrageMint(ctx: BlockchainContext): Unit = {
println("---- Start ArbitrageMint scenario !! ----")
val ORACLE_ERG_USD_PRICE = 584663187L
Expand All @@ -167,7 +270,7 @@ object ContractTesting {
val WANT_DEXY = 2000L
val feeNum = 5
val feeDenom = 1000
val NEEDED_ERG: Long = WANT_DEXY*ORACLE_ERG_USD_PRICE * (feeDenom+feeNum+1)/feeDenom
val NEEDED_ERG: Long = WANT_DEXY * ORACLE_ERG_USD_PRICE * (feeDenom + feeNum + 1) / feeDenom
val DEXY_RESERVES = 16000L
val USER_FOUND: Long = 4000e9.toLong
val BASE_FREE_MINT_VALUE: Long = 2e9.toLong
Expand All @@ -184,7 +287,7 @@ object ContractTesting {
.contract(new ErgoTreeContract(dexyAddresses.lpAddress.script))
.value(BASE_LP_BOX_VALUE)
.tokens(DexyTokenObj.lpNFT, DexyTokenObj.LPToken, new ErgoToken(DexyToken.dexyUSDToken, DEXY_RESERVES))
.registers(ErgoValue.of(ORACLE_ERG_USD_PRICE),ErgoValue.of(LB_BOX_R5))
.registers(ErgoValue.of(ORACLE_ERG_USD_PRICE), ErgoValue.of(LB_BOX_R5))
.build()
.convertToInputWith("adfd114c8b145dd758248c9dadf818927da3a7cd56bbe4129ba053d1ce0cbc30", 0)

Expand Down Expand Up @@ -260,4 +363,111 @@ object ContractTesting {
println(s"signed tx: ${signed.toJson(false)}")
println("---- ArbitrageMint scenario Done !! ----")
}

def arbitrageMintCounterReset(ctx: BlockchainContext): Unit = {
println("---- Start ArbitrageMint with CounterReset scenario !! ----")
val ORACLE_ERG_USD_PRICE = 584663187L
val RESET_HEIGHT: Int = 1000
val REMAINING_DEXY_USD: Long = 1e15.toLong
val WANT_DEXY = 2000L
val feeNum = 5
val feeDenom = 1000
val NEEDED_ERG: Long = WANT_DEXY * ORACLE_ERG_USD_PRICE * (feeDenom + feeNum + 1) / feeDenom
val DEXY_RESERVES = 16000L
val USER_FOUND: Long = 4000e9.toLong
val BASE_FREE_MINT_VALUE: Long = 2e9.toLong
val BASE_LP_BOX_VALUE: Long = 10806126325195L
val DEXY_FEE: Long = 1e9.toLong // TODO: This value is just for testing purpose and isn't true
val LB_BOX_R5: Int = 859700
val t_arb: Int = 30
val oracleRate: Long = ORACLE_ERG_USD_PRICE * (1000 + 5) / 1000
val maxAllowedIfReset: Long = (BASE_LP_BOX_VALUE - (oracleRate * DEXY_RESERVES)) / oracleRate
println(maxAllowedIfReset)
println(WANT_DEXY)
// dummy prover with dummy secret
val prover = ctx.newProverBuilder()
.withDLogSecret(BigInt.apply(0).bigInteger)
.build()

val tb = ctx.newTxBuilder()
val lpBox = tb.outBoxBuilder()
.contract(new ErgoTreeContract(dexyAddresses.lpAddress.script))
.value(BASE_LP_BOX_VALUE)
.tokens(DexyTokenObj.lpNFT, DexyTokenObj.LPToken, new ErgoToken(DexyToken.dexyUSDToken, DEXY_RESERVES))
.registers(ErgoValue.of(ORACLE_ERG_USD_PRICE), ErgoValue.of(LB_BOX_R5))
.build()
.convertToInputWith("adfd114c8b145dd758248c9dadf818927da3a7cd56bbe4129ba053d1ce0cbc30", 0)

val oracleBox = tb.outBoxBuilder()
.contract(new ErgoTreeContract(Address.create("4MQyML64GnzMxZgm").getErgoAddress.script))
.value(2e9.toLong)
.tokens(DexyTokenObj.oracleNFT)
.registers(ErgoValue.of(ORACLE_ERG_USD_PRICE))
.build()
.convertToInputWith("96be1e2f79bfb4ef587b4480966efe9e90f0dddd89c616a1f65222ea2ac4e351", 0)

val bankBox = tb.outBoxBuilder()
.contract(new ErgoTreeContract(dexyAddresses.bankAddress.script))
.value((11610).toLong)
.tokens(DexyTokenObj.bankNFT, DexyTokenObj.dexyUSDToken)
.build()
.convertToInputWith("ce552663312afc2379a91f803c93e2b10b424f176fbc930055c10def2fd88a5d", 0)

val arbitrageMintBox = tb.outBoxBuilder()
.contract(new ErgoTreeContract(dexyAddresses.arbitrageMintAddress.script))
.value(BASE_FREE_MINT_VALUE)
.tokens(DexyTokenObj.arbitrageMintNFT)
.registers(ErgoValue.of(RESET_HEIGHT), ErgoValue.of(REMAINING_DEXY_USD))
.build()
.convertToInputWith("55c0537aafa6932c9a25c856fcd064d317af96a4fa733012c26d836bbaf5689a", 1)

val userInBox = tb.outBoxBuilder()
.contract(new ErgoTreeContract(Address.create("4MQyML64GnzMxZgm").getErgoAddress.script))
.value(USER_FOUND)
.build()
.convertToInputWith("b84e555e05181c1c8cf661535bb197853fe4ec2299f2fd399ba7bb1e73f5e69b", 0)

val newBankBox = tb.outBoxBuilder()
.contract(new ErgoTreeContract(dexyAddresses.bankAddress.script))
.value(bankBox.getValue + NEEDED_ERG)
.tokens(DexyTokenObj.bankNFT, new ErgoToken(DexyToken.dexyUSDToken, bankBox.getTokens.get(1).getValue - WANT_DEXY))
.build()

val newArbitrageMintBox = tb.outBoxBuilder()
.contract(new ErgoTreeContract(dexyAddresses.arbitrageMintAddress.script))
.value(BASE_FREE_MINT_VALUE + DEXY_FEE)
.tokens(DexyTokenObj.arbitrageMintNFT)
.registers(ErgoValue.of(ctx.getHeight + t_arb), ErgoValue.of(maxAllowedIfReset - WANT_DEXY))
.build()

val userOutBox = tb.outBoxBuilder()
.contract(new ErgoTreeContract(Address.create("4MQyML64GnzMxZgm").getErgoAddress.script))
.value(USER_FOUND - NEEDED_ERG - FEE - DEXY_FEE)
.tokens(new ErgoToken(DexyToken.dexyUSDToken, WANT_DEXY))
.build()

val tx = tb.boxesToSpend(Seq(
arbitrageMintBox,
bankBox,
userInBox
).asJava)
.fee(FEE)
.outputs(newArbitrageMintBox, newBankBox, userOutBox)
.withDataInputs(List(oracleBox, lpBox).asJava)
.sendChangeTo(Address.create("4MQyML64GnzMxZgm").getErgoAddress)
.build()

var signed: SignedTransaction = null
try {
signed = prover.sign(tx)
} catch {
case e: Exception => {
println("User could not sing tx due to:")
println(e)
return
}
}
println(s"signed tx: ${signed.toJson(false)}")
println("---- ArbitrageMint with CounterReset scenario Done !! ----")
}
}
9 changes: 6 additions & 3 deletions src/main/scala/DexyContracts.scala
Original file line number Diff line number Diff line change
Expand Up @@ -297,7 +297,7 @@ object DexyContracts {
| val validSuccessorR4 = successorR4 == (if (isCounterReset) HEIGHT + t_free else selfInR4)
| val validSuccessorR5 = successorR5 == availableToMint - dexyMinted
|
| val validBankBoxInOut = bankBoxIn.tokens(0)._1 == bankNFT && bankBoxOut.tokens(0)._1 == bankNFT
| val validBankBoxInOut = bankBoxIn.tokens(0)._1 == bankNFT
| val validLpBox = lpBox.tokens(0)._1 == lpNFT
| val validOracleBox = oracleBox.tokens(0)._1 == oracleNFT
| val validSuccessor = successor.tokens == SELF.tokens && // NFT preserved
Expand Down Expand Up @@ -381,7 +381,10 @@ object DexyContracts {
|
| val dexyInCirculation = ${initialDexyTokens}L - bankDexy
|
| // referring to the section "4 Worst Scenario and Bank Reserves" in paper, the following notation is used
| // We have the parameter payoutThreshold for ensuring that the bank has at least that many ergs
| // before payout. However, we need to also ensure that there are enough ergs in the bank to
| // cover the "worst case scenario" described in section "4 Worst Scenario and Bank Reserves" in paper,
| // The following notation is used
| val O = dexyInCirculation // (note: capital o, not zero)
| val p = lpRate // initial rate
| val s = oracleRate // final lower rate after crash
Expand Down Expand Up @@ -708,7 +711,7 @@ object DexyContracts {
| val resetNow = trackerHeightOut == ${Int.MaxValue} // Infinity
|
| val trigger = ((isBelowIn && x < y) || (!isBelowIn && x > y)) && notTriggeredEarlier && triggeredNow
| val reset = (isBelowIn && x >= y) || (!isBelowIn && x <= y) && notResetEarlier && resetNow
| val reset = ((isBelowIn && x >= y) || (!isBelowIn && x <= y)) && notResetEarlier && resetNow
| val correctAction = trigger || reset
|
| numOut == numIn &&
Expand Down