mirror of
https://github.com/pezkuwichain/pezkuwi-wallet-android.git
synced 2026-04-23 00:17:56 +00:00
Initial commit: Pezkuwi Wallet Android
Complete rebrand of Nova Wallet for Pezkuwichain ecosystem. ## Features - Full Pezkuwichain support (HEZ & PEZ tokens) - Polkadot ecosystem compatibility - Staking, Governance, DeFi, NFTs - XCM cross-chain transfers - Hardware wallet support (Ledger, Polkadot Vault) - WalletConnect v2 - Push notifications ## Languages - English, Turkish, Kurmanci (Kurdish), Spanish, French, German, Russian, Japanese, Chinese, Korean, Portuguese, Vietnamese Based on Nova Wallet by Novasama Technologies GmbH © Dijital Kurdistan Tech Institute 2026
This commit is contained in:
+274
@@ -0,0 +1,274 @@
|
||||
package io.novafoundation.nova.feature_governance_api.domain.locks
|
||||
|
||||
import io.novafoundation.nova.common.data.network.runtime.binding.BlockNumber
|
||||
import io.novafoundation.nova.feature_governance_api.data.network.blockhain.model.AccountVote
|
||||
import io.novafoundation.nova.feature_governance_api.data.network.blockhain.model.AyeVote
|
||||
import io.novafoundation.nova.feature_governance_api.data.network.blockhain.model.OnChainReferendum
|
||||
import io.novafoundation.nova.feature_governance_api.data.network.blockhain.model.OnChainReferendumStatus
|
||||
import io.novafoundation.nova.feature_governance_api.data.network.blockhain.model.PriorLock
|
||||
import io.novafoundation.nova.feature_governance_api.data.network.blockhain.model.ReferendumId
|
||||
import io.novafoundation.nova.feature_governance_api.data.network.blockhain.model.TrackId
|
||||
import io.novafoundation.nova.feature_governance_api.data.network.blockhain.model.Voting
|
||||
import io.novafoundation.nova.feature_governance_api.domain.locks.ClaimSchedule.ClaimAction
|
||||
import io.novafoundation.nova.feature_governance_api.domain.locks.ClaimSchedule.UnlockChunk
|
||||
import io.novafoundation.nova.feature_wallet_api.data.network.blockhain.types.Balance
|
||||
import io.novafoundation.nova.runtime.multiNetwork.runtime.types.custom.vote.Conviction
|
||||
import io.novasama.substrate_sdk_android.runtime.AccountId
|
||||
|
||||
interface ClaimScheduleTestBuilder {
|
||||
|
||||
fun given(builder: Given.() -> Unit)
|
||||
|
||||
interface Given {
|
||||
|
||||
fun currentBlock(block: Int)
|
||||
|
||||
fun track(trackId: Int, builder: Track.() -> Unit)
|
||||
|
||||
interface Track {
|
||||
|
||||
fun lock(lock: Int)
|
||||
|
||||
fun voting(builder: Voting.() -> Unit)
|
||||
|
||||
fun delegating(builder: Delegating.() -> Unit)
|
||||
|
||||
interface Voting {
|
||||
|
||||
fun prior(amount: Int, unlockAt: Int)
|
||||
|
||||
fun vote(amount: Int, referendumId: Int, unlockAt: Int)
|
||||
}
|
||||
|
||||
interface Delegating {
|
||||
|
||||
fun prior(amount: Int, unlockAt: Int)
|
||||
|
||||
fun delegate(amount: Int)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun expect(builder: Expect.() -> Unit)
|
||||
|
||||
interface Expect {
|
||||
|
||||
fun claimable(amount: Int, actions: ClaimableActions.() -> Unit)
|
||||
|
||||
fun nonClaimable(amount: Int, claimAt: Int)
|
||||
|
||||
fun nonClaimable(amount: Int)
|
||||
|
||||
interface ClaimableActions {
|
||||
|
||||
fun unlock(trackId: Int)
|
||||
|
||||
fun removeVote(trackId: Int, referendumId: Int)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun ClaimScheduleTest(builder: ClaimScheduleTestBuilder.() -> Unit) {
|
||||
val test = ClaimScheduleTest().apply(builder)
|
||||
|
||||
test.runTest()
|
||||
}
|
||||
|
||||
private class ClaimScheduleTest : ClaimScheduleTestBuilder {
|
||||
|
||||
private var calculator: RealClaimScheduleCalculator? = null
|
||||
private var expectedSchedule: ClaimSchedule? = null
|
||||
|
||||
override fun given(builder: ClaimScheduleTestBuilder.Given.() -> Unit) {
|
||||
calculator = GivenBuilder().apply(builder).build()
|
||||
}
|
||||
|
||||
override fun expect(builder: ClaimScheduleTestBuilder.Expect.() -> Unit) {
|
||||
expectedSchedule = ExpectedBuilder().apply(builder).buildSchedule()
|
||||
}
|
||||
|
||||
fun runTest() {
|
||||
val actualSchedule = calculator!!.estimateClaimSchedule()
|
||||
|
||||
assert(actualSchedule == expectedSchedule!!) {
|
||||
buildString {
|
||||
append("Expected schedule: $expectedSchedule\n")
|
||||
append("Actual schedule : $actualSchedule\n")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class ExpectedBuilder : ClaimScheduleTestBuilder.Expect {
|
||||
|
||||
private var chunks = mutableListOf<UnlockChunk>()
|
||||
|
||||
override fun claimable(amount: Int, actionsBuilder: ClaimScheduleTestBuilder.Expect.ClaimableActions.() -> Unit) {
|
||||
val actions = ClaimableActionsBuilder().apply(actionsBuilder).buildActions()
|
||||
|
||||
chunks.add(UnlockChunk.Claimable(amount.toBigInteger(), actions))
|
||||
}
|
||||
|
||||
override fun nonClaimable(amount: Int, claimAt: Int) {
|
||||
chunks.add(UnlockChunk.Pending(amount.toBigInteger(), ClaimTime.At(claimAt.toBigInteger())))
|
||||
}
|
||||
|
||||
override fun nonClaimable(amount: Int) {
|
||||
chunks.add(UnlockChunk.Pending(amount.toBigInteger(), ClaimTime.UntilAction))
|
||||
}
|
||||
|
||||
fun buildSchedule(): ClaimSchedule {
|
||||
return ClaimSchedule(chunks)
|
||||
}
|
||||
}
|
||||
|
||||
private class ClaimableActionsBuilder : ClaimScheduleTestBuilder.Expect.ClaimableActions {
|
||||
|
||||
private val actions = mutableListOf<ClaimAction>()
|
||||
|
||||
|
||||
override fun unlock(trackId: Int) {
|
||||
val action = ClaimAction.Unlock(TrackId(trackId.toBigInteger()))
|
||||
actions.add(action)
|
||||
}
|
||||
|
||||
override fun removeVote(trackId: Int, referendumId: Int) {
|
||||
val trackIdTyped = TrackId(trackId.toBigInteger())
|
||||
val referendumIdTyped = ReferendumId(referendumId.toBigInteger())
|
||||
|
||||
val action = ClaimAction.RemoveVote(trackIdTyped, referendumIdTyped)
|
||||
actions.add(action)
|
||||
}
|
||||
|
||||
fun buildActions(): List<ClaimAction> {
|
||||
return actions
|
||||
}
|
||||
}
|
||||
|
||||
private class GivenBuilder : ClaimScheduleTestBuilder.Given {
|
||||
|
||||
private var voting: MutableMap<TrackId, Voting> = mutableMapOf()
|
||||
private var currentBlockNumber: BlockNumber = BlockNumber.ZERO
|
||||
private var referenda: MutableMap<ReferendumId, OnChainReferendum> = mutableMapOf()
|
||||
private var trackLocks: MutableMap<TrackId, Balance> = mutableMapOf()
|
||||
|
||||
override fun currentBlock(block: Int) {
|
||||
currentBlockNumber = block.toBigInteger()
|
||||
}
|
||||
|
||||
override fun track(trackId: Int, builder: ClaimScheduleTestBuilder.Given.Track.() -> Unit) {
|
||||
val trackIdTyped = TrackId(trackId.toBigInteger())
|
||||
val builtTrack = TrackBuilder().apply(builder)
|
||||
|
||||
voting[trackIdTyped] = builtTrack.buildVoting()
|
||||
|
||||
val newReferenda = builtTrack.buildReferendaApprovedAt().mapValues { (referendaId, approvedAt) ->
|
||||
OnChainReferendum(
|
||||
id = referendaId,
|
||||
status = OnChainReferendumStatus.Approved(since = approvedAt)
|
||||
)
|
||||
}
|
||||
referenda += newReferenda
|
||||
|
||||
trackLocks[trackIdTyped] = builtTrack.buildTrackLock()
|
||||
}
|
||||
|
||||
fun build(): RealClaimScheduleCalculator {
|
||||
return RealClaimScheduleCalculator(
|
||||
votingByTrack = voting,
|
||||
currentBlockNumber = currentBlockNumber,
|
||||
referenda = referenda,
|
||||
trackLocks = trackLocks,
|
||||
|
||||
// those parameters are only used for ongoing referenda estimation
|
||||
// we only use approved ones in this tests
|
||||
tracks = emptyMap(),
|
||||
undecidingTimeout = BlockNumber.ZERO,
|
||||
|
||||
// we do not use conviction in tests
|
||||
voteLockingPeriod = BlockNumber.ZERO
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private fun PriorLock(): PriorLock = PriorLock(BlockNumber.ZERO, Balance.ZERO)
|
||||
|
||||
private class VotingBuilder : ClaimScheduleTestBuilder.Given.Track.Voting {
|
||||
|
||||
private var prior: PriorLock = PriorLock()
|
||||
private val votes = mutableMapOf<ReferendumId, AccountVote>()
|
||||
private var referendumApprovedAt = mutableMapOf<ReferendumId, BlockNumber>()
|
||||
override fun prior(amount: Int, unlockAt: Int) {
|
||||
prior = PriorLock(unlockAt = unlockAt.toBigInteger(), amount = amount.toBigInteger())
|
||||
}
|
||||
|
||||
override fun vote(amount: Int, referendumId: Int, unlockAt: Int) {
|
||||
val referendumIdTyped = ReferendumId(referendumId.toBigInteger())
|
||||
votes[referendumIdTyped] = AyeVote(amount.toBigInteger(), Conviction.None)
|
||||
referendumApprovedAt[referendumIdTyped] = unlockAt.toBigInteger()
|
||||
}
|
||||
|
||||
fun buildReferendaApprovedAt(): Map<ReferendumId, BlockNumber> {
|
||||
return referendumApprovedAt
|
||||
}
|
||||
|
||||
fun build(): Voting.Casting = Voting.Casting(votes, prior)
|
||||
}
|
||||
|
||||
private class DelegatingBuilder : ClaimScheduleTestBuilder.Given.Track.Delegating {
|
||||
|
||||
private var prior: PriorLock = PriorLock()
|
||||
private var delegation: Balance? = null
|
||||
override fun prior(amount: Int, unlockAt: Int) {
|
||||
prior = PriorLock(unlockAt = unlockAt.toBigInteger(), amount = amount.toBigInteger())
|
||||
}
|
||||
|
||||
override fun delegate(amount: Int) {
|
||||
delegation = amount.toBigInteger()
|
||||
}
|
||||
|
||||
fun build(): Voting.Delegating {
|
||||
return Voting.Delegating(
|
||||
amount = requireNotNull(delegation),
|
||||
target = AccountId(32),
|
||||
conviction = Conviction.None, // we don't use conviction since it doesn't matter until undelegated
|
||||
prior = prior,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private class TrackBuilder : ClaimScheduleTestBuilder.Given.Track {
|
||||
|
||||
private var trackLock: Balance = Balance.ZERO
|
||||
|
||||
private var voting: Voting = Voting.Casting(emptyMap(), PriorLock())
|
||||
private var referendumApprovedAt = mapOf<ReferendumId, BlockNumber>()
|
||||
|
||||
override fun lock(lock: Int) {
|
||||
trackLock = lock.toBigInteger()
|
||||
}
|
||||
|
||||
override fun voting(builder: ClaimScheduleTestBuilder.Given.Track.Voting.() -> Unit) {
|
||||
val votingBuilder = VotingBuilder().apply(builder)
|
||||
|
||||
voting = votingBuilder.build()
|
||||
referendumApprovedAt = votingBuilder.buildReferendaApprovedAt()
|
||||
}
|
||||
|
||||
override fun delegating(builder: ClaimScheduleTestBuilder.Given.Track.Delegating.() -> Unit) {
|
||||
voting = DelegatingBuilder().apply(builder).build()
|
||||
}
|
||||
|
||||
fun buildVoting(): Voting {
|
||||
return voting
|
||||
}
|
||||
|
||||
fun buildReferendaApprovedAt(): Map<ReferendumId, BlockNumber> {
|
||||
return referendumApprovedAt
|
||||
}
|
||||
|
||||
fun buildTrackLock(): Balance {
|
||||
return trackLock
|
||||
}
|
||||
}
|
||||
+480
@@ -0,0 +1,480 @@
|
||||
package io.novafoundation.nova.feature_governance_api.domain.locks
|
||||
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.mockito.junit.MockitoJUnitRunner
|
||||
|
||||
@RunWith(MockitoJUnitRunner::class)
|
||||
class RealClaimScheduleCalculatorTest {
|
||||
|
||||
@Test
|
||||
fun `should handle empty case`() = ClaimScheduleTest {
|
||||
given {
|
||||
}
|
||||
|
||||
expect {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should handle single claimable`() = ClaimScheduleTest {
|
||||
given {
|
||||
currentBlock(1000)
|
||||
|
||||
track(0) {
|
||||
voting {
|
||||
vote(amount = 1, referendumId = 0, unlockAt = 1000)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
expect {
|
||||
claimable(amount = 1) {
|
||||
removeVote(trackId = 0, referendumId = 0)
|
||||
unlock(trackId = 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should handle both passed and not priors`() = ClaimScheduleTest {
|
||||
given {
|
||||
currentBlock(1000)
|
||||
|
||||
track(0) {
|
||||
voting {
|
||||
prior(amount = 2, unlockAt = 1000)
|
||||
}
|
||||
}
|
||||
|
||||
track(1) {
|
||||
voting {
|
||||
prior(amount = 1, unlockAt = 1100)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
expect {
|
||||
claimable(amount = 1) {
|
||||
unlock(trackId = 0)
|
||||
}
|
||||
|
||||
nonClaimable(amount = 1, claimAt = 1100)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should extend votes by prior`() = ClaimScheduleTest {
|
||||
given {
|
||||
currentBlock(1000)
|
||||
|
||||
track(0) {
|
||||
voting {
|
||||
prior(amount = 1, unlockAt = 1100)
|
||||
|
||||
vote(amount = 2, unlockAt = 1000, referendumId = 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
expect {
|
||||
nonClaimable(amount = 2, claimAt = 1100)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should take max between two locks with same time`() = ClaimScheduleTest {
|
||||
given {
|
||||
currentBlock(1000)
|
||||
|
||||
track(0) {
|
||||
voting {
|
||||
vote(amount = 8, referendumId = 0, unlockAt = 1000)
|
||||
vote(amount = 2, referendumId = 1, unlockAt = 1000)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
expect {
|
||||
claimable(amount = 8) {
|
||||
removeVote(trackId = 0, referendumId = 0)
|
||||
removeVote(trackId = 0, referendumId = 1)
|
||||
unlock(trackId = 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should handle rejigged prior`() = ClaimScheduleTest {
|
||||
given {
|
||||
currentBlock(1200)
|
||||
|
||||
track(0) {
|
||||
voting {
|
||||
prior(amount = 1, unlockAt = 1100)
|
||||
|
||||
vote(amount = 2, unlockAt = 1000, referendumId = 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
expect {
|
||||
claimable(amount = 2) {
|
||||
removeVote(trackId = 0, referendumId = 1)
|
||||
unlock(trackId = 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should fold several claimable to one`() = ClaimScheduleTest {
|
||||
given {
|
||||
currentBlock(1100)
|
||||
|
||||
track(0) {
|
||||
lock(0)
|
||||
|
||||
voting {
|
||||
vote(amount = 1, referendumId = 0, unlockAt = 1100)
|
||||
}
|
||||
}
|
||||
|
||||
track(1) {
|
||||
lock(0)
|
||||
|
||||
voting {
|
||||
vote(amount = 2, referendumId = 1, unlockAt = 1000)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
expect {
|
||||
claimable(amount = 2) {
|
||||
removeVote(trackId = 1, referendumId = 1)
|
||||
removeVote(trackId = 0, referendumId = 0)
|
||||
|
||||
unlock(trackId = 1)
|
||||
unlock(trackId = 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
fun `should include shadowed actions`() = ClaimScheduleTest {
|
||||
given {
|
||||
currentBlock(1200)
|
||||
|
||||
track(1) {
|
||||
lock(0)
|
||||
|
||||
voting {
|
||||
vote(amount = 1, referendumId = 1, unlockAt = 1000)
|
||||
}
|
||||
}
|
||||
|
||||
track(2) {
|
||||
lock(0)
|
||||
|
||||
voting {
|
||||
vote(amount = 2, referendumId = 2, unlockAt = 1100)
|
||||
}
|
||||
}
|
||||
|
||||
track(3) {
|
||||
lock(0)
|
||||
|
||||
voting {
|
||||
vote(1, referendumId = 3, unlockAt = 1200)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
expect {
|
||||
claimable(amount = 2) {
|
||||
removeVote(trackId = 2, referendumId = 2)
|
||||
removeVote(trackId = 1, referendumId = 1)
|
||||
removeVote(trackId = 3, referendumId = 3)
|
||||
|
||||
unlock(2)
|
||||
unlock(1)
|
||||
unlock(3)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should take gap into account`() = ClaimScheduleTest {
|
||||
given {
|
||||
currentBlock(1000)
|
||||
|
||||
track(0) {
|
||||
lock(10)
|
||||
|
||||
voting {
|
||||
vote(amount = 2, referendumId = 0, unlockAt = 1000)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
expect {
|
||||
claimable(amount = 10) {
|
||||
removeVote(trackId = 0, referendumId = 0)
|
||||
unlock(trackId = 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
fun `gap should be limited with other locks`() = ClaimScheduleTest {
|
||||
given {
|
||||
currentBlock(1000)
|
||||
|
||||
track(0) {
|
||||
lock(10)
|
||||
|
||||
voting {
|
||||
vote(amount = 1, referendumId = 0, unlockAt = 1000)
|
||||
}
|
||||
}
|
||||
|
||||
track(1) {
|
||||
voting {
|
||||
prior(amount = 10, unlockAt = 1000)
|
||||
}
|
||||
}
|
||||
|
||||
track(2) {
|
||||
voting {
|
||||
prior(amount = 1, unlockAt = 1100)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
expect {
|
||||
claimable(amount = 9) {
|
||||
removeVote(trackId = 0, referendumId = 0)
|
||||
unlock(trackId = 0)
|
||||
|
||||
unlock(trackId = 1)
|
||||
}
|
||||
|
||||
nonClaimable(amount = 1, claimAt = 1100)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `gap claim should be delayed`() = ClaimScheduleTest {
|
||||
given {
|
||||
currentBlock(1000)
|
||||
|
||||
track(0) {
|
||||
lock(10)
|
||||
}
|
||||
|
||||
track(1) {
|
||||
voting {
|
||||
prior(amount = 10, unlockAt = 1100)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
expect {
|
||||
nonClaimable(amount = 10, claimAt = 1100)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
fun `should not dublicate unlock command with both prior and gap present`() = ClaimScheduleTest {
|
||||
given {
|
||||
currentBlock(1100)
|
||||
|
||||
track(0) {
|
||||
lock(10)
|
||||
|
||||
voting {
|
||||
prior(amount = 5, unlockAt = 1050)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
expect {
|
||||
claimable(amount = 10) {
|
||||
unlock(trackId = 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `pending should be sorted by remaining time`() = ClaimScheduleTest {
|
||||
given {
|
||||
currentBlock(1000)
|
||||
|
||||
track(0) {
|
||||
voting {
|
||||
vote(amount = 3, unlockAt = 1100, referendumId = 0)
|
||||
vote(amount = 2, unlockAt = 1200, referendumId = 2)
|
||||
vote(amount = 1, unlockAt = 1300, referendumId = 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
expect {
|
||||
nonClaimable(amount = 1, claimAt = 1100)
|
||||
nonClaimable(amount = 1, claimAt = 1200)
|
||||
nonClaimable(amount = 1, claimAt = 1300)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `gap should not be covered by its track locks`() = ClaimScheduleTest {
|
||||
given {
|
||||
currentBlock(1000)
|
||||
|
||||
track(20) {
|
||||
lock(1)
|
||||
|
||||
voting {
|
||||
vote(amount = 1, unlockAt = 2000, referendumId = 13)
|
||||
}
|
||||
}
|
||||
|
||||
track(21) {
|
||||
// gap is 101 - 10 = 91 - should not be delayed by its own track voting
|
||||
lock(101)
|
||||
|
||||
voting {
|
||||
vote(amount = 10, unlockAt = 1500, referendumId = 5)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
expect {
|
||||
claimable(amount = 91) {
|
||||
unlock(21)
|
||||
}
|
||||
|
||||
nonClaimable(amount = 9, claimAt = 1500)
|
||||
nonClaimable(amount = 1, claimAt = 2000)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should handle standalone delegation`() = ClaimScheduleTest{
|
||||
given {
|
||||
track(0) {
|
||||
delegating {
|
||||
delegate(1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
expect {
|
||||
nonClaimable(amount = 1)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should take delegation prior lock into account`() = ClaimScheduleTest{
|
||||
given {
|
||||
currentBlock(1000)
|
||||
|
||||
track(0) {
|
||||
delegating {
|
||||
prior(amount = 10, unlockAt = 1100)
|
||||
|
||||
delegate(1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
expect {
|
||||
nonClaimable(amount = 9, claimAt = 1100) // prior is 10, but 1 is delayed because of delegation
|
||||
nonClaimable(amount = 1)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `delegation plus gap case`() = ClaimScheduleTest{
|
||||
given {
|
||||
currentBlock(1000)
|
||||
|
||||
track(0) {
|
||||
lock(10)
|
||||
|
||||
delegating {
|
||||
delegate(1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
expect {
|
||||
claimable(amount = 9) {
|
||||
unlock(0)
|
||||
}
|
||||
nonClaimable(amount = 1)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `delegate plus voting case`() = ClaimScheduleTest{
|
||||
given {
|
||||
currentBlock(1000)
|
||||
|
||||
track(0) {
|
||||
delegating {
|
||||
delegate(1)
|
||||
}
|
||||
}
|
||||
|
||||
track(1) {
|
||||
voting {
|
||||
prior(10, unlockAt = 1000)
|
||||
|
||||
vote(amount = 5, unlockAt = 1100, referendumId = 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
expect {
|
||||
|
||||
// 5 is claimable from track 1 priors
|
||||
claimable(amount = 5) {
|
||||
unlock(1)
|
||||
}
|
||||
// 4 is delayed until 1100 from track 1 votes
|
||||
nonClaimable(amount = 4, claimAt = 1100)
|
||||
|
||||
// 1 is delayed indefinitely because of track 1 delegation
|
||||
nonClaimable(amount = 1)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should not dublicate unlcock when claiming multiple chunks`() = ClaimScheduleTest {
|
||||
given {
|
||||
currentBlock(1100)
|
||||
|
||||
track(1) {
|
||||
lock(10)
|
||||
|
||||
voting {
|
||||
vote(amount = 5, unlockAt = 1002, referendumId = 2)
|
||||
vote(amount = 10, unlockAt = 1001, referendumId = 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
expect {
|
||||
claimable(amount = 10) {
|
||||
removeVote(trackId = 1, referendumId = 1)
|
||||
removeVote(trackId = 1, referendumId = 2)
|
||||
|
||||
unlock(trackId = 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
package io.novafoundation.nova.feature_governance_impl.data.model.curve
|
||||
|
||||
import io.novafoundation.nova.common.data.network.runtime.binding.Perbill
|
||||
import io.novafoundation.nova.common.utils.hasTheSaveValueAs
|
||||
import io.novafoundation.nova.common.utils.percentageToFraction
|
||||
import io.novafoundation.nova.feature_governance_api.data.network.blockhain.model.VotingCurve
|
||||
import org.junit.Assert
|
||||
import java.math.BigDecimal
|
||||
import java.math.BigInteger
|
||||
import java.math.MathContext
|
||||
|
||||
val Int.percent
|
||||
get() = this.toBigDecimal().percentageToFraction()
|
||||
|
||||
fun VotingCurve.runThresholdTests(tests: List<Pair<Perbill, Perbill>>) {
|
||||
tests.forEach { (x, expectedY) ->
|
||||
val y = threshold(x)
|
||||
Assert.assertTrue("Expected: ${expectedY}, got: ${y}", expectedY hasTheSaveValueAs y)
|
||||
}
|
||||
}
|
||||
|
||||
fun VotingCurve.runDelayTests(tests: List<Pair<Perbill, Perbill>>) {
|
||||
tests.forEach { (x, expectedY) ->
|
||||
val y = delay(x)
|
||||
Assert.assertTrue("Expected $expectedY for input $x but got: $y", expectedY hasTheSaveValueAs y)
|
||||
}
|
||||
}
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
package io.novafoundation.nova.feature_governance_impl.data.model.curve
|
||||
|
||||
import io.novafoundation.nova.feature_governance_api.data.thresold.gov2.curve.LinearDecreasingCurve
|
||||
import org.junit.Test
|
||||
|
||||
class LinearDecreasingCurveTest {
|
||||
|
||||
val curve = LinearDecreasingCurve(
|
||||
ceil = 90.percent,
|
||||
floor = 10.percent,
|
||||
length = 50.percent,
|
||||
)
|
||||
|
||||
// x to y
|
||||
private val THRESHOLD_TESTS = listOf(
|
||||
0.percent to 90.percent,
|
||||
25.percent to 50.percent,
|
||||
50.percent to 10.percent,
|
||||
100.percent to 10.percent
|
||||
)
|
||||
|
||||
// y to x
|
||||
private val DELAY_TESTS = listOf(
|
||||
100.percent to 0.percent,
|
||||
90.percent to 0.percent,
|
||||
50.percent to 25.percent,
|
||||
10.percent to 50.percent,
|
||||
9.percent to 100.percent,
|
||||
0.percent to 100.percent
|
||||
)
|
||||
|
||||
@Test
|
||||
fun threshold() {
|
||||
curve.runThresholdTests(THRESHOLD_TESTS)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun delay() {
|
||||
curve.runDelayTests(DELAY_TESTS)
|
||||
}
|
||||
}
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
package io.novafoundation.nova.feature_governance_impl.data.model.curve
|
||||
|
||||
import io.novafoundation.nova.feature_governance_api.data.thresold.gov2.curve.ReciprocalCurve
|
||||
import org.junit.Test
|
||||
import java.math.BigDecimal
|
||||
|
||||
class ReciprocalCurveTest {
|
||||
|
||||
// 10/(x + 1) - 1
|
||||
val curve = ReciprocalCurve(
|
||||
factor = BigDecimal.TEN,
|
||||
xOffset = BigDecimal.ONE,
|
||||
yOffset = (-1).toBigDecimal()
|
||||
)
|
||||
|
||||
// x to y
|
||||
private val TESTS = listOf(
|
||||
BigDecimal.ZERO to 9.toBigDecimal(),
|
||||
0.25.toBigDecimal() to 7.toBigDecimal(),
|
||||
BigDecimal.ONE to 4.toBigDecimal(),
|
||||
3.toBigDecimal() to 1.5.toBigDecimal()
|
||||
)
|
||||
|
||||
// y to x
|
||||
private val DELAY_TESTS = listOf(
|
||||
9.toBigDecimal() to BigDecimal.ZERO,
|
||||
7.toBigDecimal() to 0.25.toBigDecimal(),
|
||||
4.toBigDecimal() to BigDecimal.ONE
|
||||
)
|
||||
|
||||
@Test
|
||||
fun threshold() {
|
||||
curve.runThresholdTests(TESTS)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun delay() {
|
||||
curve.runDelayTests(DELAY_TESTS)
|
||||
}
|
||||
}
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
package io.novafoundation.nova.feature_governance_impl.data.model.curve
|
||||
|
||||
import io.novafoundation.nova.common.utils.lessEpsilon
|
||||
import io.novafoundation.nova.feature_governance_api.data.thresold.gov2.curve.LinearDecreasingCurve
|
||||
import io.novafoundation.nova.feature_governance_api.data.thresold.gov2.curve.SteppedDecreasingCurve
|
||||
import java.math.BigDecimal
|
||||
import org.junit.Test
|
||||
|
||||
class SteppedDecreasingCurveTest {
|
||||
|
||||
val curve = SteppedDecreasingCurve(
|
||||
begin = 80.percent,
|
||||
end = 30.percent,
|
||||
step = 10.percent,
|
||||
period = 15.percent
|
||||
)
|
||||
|
||||
// x to y
|
||||
private val TESTS = listOf(
|
||||
0.percent to 80.percent,
|
||||
15.percent.lessEpsilon() to 80.percent,
|
||||
15.percent to 70.percent,
|
||||
30.percent.lessEpsilon() to 70.percent,
|
||||
30.percent to 60.percent,
|
||||
100.percent to 30.percent
|
||||
)
|
||||
|
||||
// y to x
|
||||
private val DELAY_TESTS = listOf(
|
||||
80.percent to 0.percent,
|
||||
70.percent to 15.percent,
|
||||
60.percent to 30.percent,
|
||||
30.percent to 75.percent,
|
||||
10.percent to 100.percent
|
||||
)
|
||||
|
||||
@Test
|
||||
fun threshold() {
|
||||
curve.runThresholdTests(TESTS)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun delay() {
|
||||
curve.runDelayTests(DELAY_TESTS)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user