Skip to content

Commit

Permalink
update naming and remove maxStake
Browse files Browse the repository at this point in the history
  • Loading branch information
mkaczanowski committed Jan 15, 2025
1 parent aa57df6 commit 2f4bb68
Show file tree
Hide file tree
Showing 4 changed files with 19 additions and 53 deletions.
12 changes: 6 additions & 6 deletions packages/staking-cli/src/cmd/ton.ts
Original file line number Diff line number Diff line change
Expand Up @@ -144,18 +144,18 @@ async function runTx (
if (!config.validatorAddress2) {
cmd.error('second validator address is required for TON Pool', { exitCode: 1, code: `${msgType}.tx.abort` })
}
const validatorAddressPair: [string, string] = [config.validatorAddress, config.validatorAddress2]
const poolsInfo = await tonStaker.getPoolAddressForStake({ validatorAddressPair })
const validatorIndex = validatorAddressPair.findIndex((addr: string) => addr === poolsInfo.SelectedPoolAddress)
if (validatorIndex === -1) {
const stakingPoolAddressPair: [string, string] = [config.validatorAddress, config.validatorAddress2]
const poolsInfo = await tonStaker.getPoolAddressForStake({ validatorAddressPair: stakingPoolAddressPair })
const poolIndex = stakingPoolAddressPair.findIndex((addr: string) => addr === poolsInfo.SelectedPoolAddress)
if (poolIndex === -1) {
cmd.error('validator address not found in the pool', { exitCode: 1, code: `${msgType}.tx.abort` })
}

console.log('Delegating to validator #' + (validatorIndex+1) + ': ' + validatorAddressPair[validatorIndex])
console.log('Delegating to pool #' + (poolIndex+1) + ': ' + stakingPoolAddressPair[poolIndex])

unsignedTx = (
await tonStaker.buildStakeTx({
validatorAddressPair,
validatorAddressPair: stakingPoolAddressPair,
amount: arg[0] // amount
})
).tx
Expand Down
42 changes: 9 additions & 33 deletions packages/ton/src/TonPoolStaker.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Address, beginCell, fromNano, toNano, Slice, Builder, DictionaryValue, Dictionary, Cell, configParse17 } from '@ton/ton'
import { Address, beginCell, fromNano, toNano, Slice, Builder, DictionaryValue, Dictionary, Cell } from '@ton/ton'
import { defaultValidUntil, getDefaultGas, getRandomQueryId, TonBaseStaker } from './TonBaseStaker'
import { UnsignedTx, Election, FrozenSet, PoolStatus, GetPoolAddressForStakeResponse } from './types'

Expand All @@ -24,15 +24,15 @@ export class TonPoolStaker extends TonBaseStaker {
validUntil?: number
}): Promise<{ tx: UnsignedTx }> {
const { validatorAddressPair, amount, validUntil, referrer } = params
const validatorAddress = (await this.getPoolAddressForStake({ validatorAddressPair })).SelectedPoolAddress
const poolAddress = (await this.getPoolAddressForStake({ validatorAddressPair })).SelectedPoolAddress

// ensure the address is for the right network
this.checkIfAddressTestnetFlagMatches(validatorAddress)
this.checkIfAddressTestnetFlagMatches(poolAddress)

// ensure the validator address is bounceable.
// NOTE: TEP-002 specifies that the address bounceable flag should match both the internal message and the address.
// This has no effect as we force the bounce flag anyway. However it is a good practice to be consistent
if (!Address.parseFriendly(validatorAddress).isBounceable) {
if (!Address.parseFriendly(poolAddress).isBounceable) {
throw new Error(
'validator address is not bounceable! It is required for nominator pool contract operations to use bounceable addresses'
)
Expand All @@ -53,7 +53,7 @@ export class TonPoolStaker extends TonBaseStaker {
const tx = {
validUntil: defaultValidUntil(validUntil),
message: {
address: validatorAddress,
address: poolAddress,
bounceable: true,
amount: toNano(amount),
payload
Expand Down Expand Up @@ -179,47 +179,35 @@ export class TonPoolStaker extends TonBaseStaker {
/** @ignore */
async getPoolAddressForStake (params: { validatorAddressPair: [string, string] }): Promise<GetPoolAddressForStakeResponse> {
const { validatorAddressPair } = params
const client = this.getClient()

// fetch required data:
// 1. stake balance for both pools
// 2. stake limits from the onchain config
// 3. last election data to get the minimum stake for participation
const [ poolOneStatus, poolTwoStatus, elections, stakeLimitsCfgCell ] = await Promise.all([
// 2. last election data to get the minimum stake for participation
const [ poolOneStatus, poolTwoStatus, elections ] = await Promise.all([
this.getPoolStatus(validatorAddressPair[0]),
this.getPoolStatus(validatorAddressPair[1]),

// elector contract address
this.getPastElections('Ef8zMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzM0vF'),

// see full config: https://tonviewer.com/config#17
client.getConfigParam(17)
])

const [ poolOneBalance, poolTwoBalance ] = [poolOneStatus.Balance, poolTwoStatus.Balance]
const stakeLimitsCfg = configParse17(stakeLimitsCfgCell.beginParse())
const maxStake = stakeLimitsCfg.maxStake

// simple sanity validation
if (elections.length == 0) {
throw new Error('No elections found')
}

if (stakeLimitsCfg.minStake == BigInt(0)) {
throw new Error('Minimum stake is 0, that is not expected')
}

// iterate lastElection.frozen and find the lowest validator stake
const lastElection = elections[0]
const values = Array.from(lastElection.frozen.values())
const minStake = values.reduce((min, p) => p.stake < min ? p.stake : min, values[0].stake)

const selectedPoolIndex = TonPoolStaker.selectPool(minStake, maxStake, [poolOneBalance, poolTwoBalance])
const selectedPoolIndex = TonPoolStaker.selectPool(minStake, [poolOneBalance, poolTwoBalance])

return {
SelectedPoolAddress: validatorAddressPair[selectedPoolIndex],
MinStake: minStake,
MaxStake: maxStake,
PoolStakes: [poolOneBalance, poolTwoBalance]
}
}
Expand Down Expand Up @@ -285,13 +273,11 @@ export class TonPoolStaker extends TonBaseStaker {
/** @ignore */
static selectPool (
minStake: bigint, // minimum stake for participation (to be in the set)
maxStake: bigint, // maximum allowes stake per validator
currentBalances: [bigint, bigint] // current stake balances of the pools
): number {
const [balancePool1, balancePool2] = currentBalances;

const hasReachedMinStake = (balance: bigint): boolean => balance >= minStake;
const hasReachedMaxStake = (balance: bigint): boolean => balance >= maxStake;

// prioritize filling a pool that hasn't reached the minStake
if (!hasReachedMinStake(balancePool1) && !hasReachedMinStake(balancePool2)) {
Expand All @@ -304,16 +290,6 @@ export class TonPoolStaker extends TonBaseStaker {
}

// both pools have reached minStake, balance them until they reach maxStake
if (!hasReachedMaxStake(balancePool1) && !hasReachedMaxStake(balancePool2)) {
// distribute to balance the pools
return balancePool1 <= balancePool2 ? 0 : 1;
} else if (!hasReachedMaxStake(balancePool1)) {
return 0; // add to pool 1 until it reaches maxStake
} else if (!hasReachedMaxStake(balancePool2)) {
return 1; // add to pool 2 until it reaches maxStake
}

// if both pools have reached maxStake, no more staking is allowed
throw new Error("Both pools have reached their maximum stake limits");
return balancePool1 <= balancePool2 ? 0 : 1;
}
}
1 change: 0 additions & 1 deletion packages/ton/src/types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,6 @@ export interface PoolStatus {
export interface GetPoolAddressForStakeResponse {
SelectedPoolAddress: string;
MinStake: bigint;
MaxStake: bigint;
PoolStakes: [biginy, bigint];
}

Expand Down
17 changes: 4 additions & 13 deletions packages/ton/test/pool-staker.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,26 +4,17 @@ import { expect } from 'chai'

describe('TonPoolStaker', () => {
it('should prioritize the pool that has not reached minStake', () => {
const result = TonPoolStaker.selectPool(200n, 1000n, [100n, 300n]);
const result = TonPoolStaker.selectPool(200n, [100n, 300n]);
expect(result).to.equal(0); // Pool 1 needs to reach minStake
})

it('should prioritize the pool with a higher balance if both are below minStake', () => {
const result = TonPoolStaker.selectPool(200n, 1000n, [100n, 150n]);
const result = TonPoolStaker.selectPool(200n, [100n, 150n]);
expect(result).to.equal(1); // Pool 2 has a higher balance
})

it('should balance the pools if both have reached minStake but are below maxStake', () => {
const result = TonPoolStaker.selectPool(200n, 1000n, [400n, 300n]);
it('should balance the pools if both have reached minStake', () => {
const result = TonPoolStaker.selectPool(200n, [400n, 300n]);
expect(result).to.equal(1); // Pool 2 has a smaller balance
})

it('should add to the pool that has not reached maxStake', () => {
const result = TonPoolStaker.selectPool(200n, 1000n, [1000n, 800n]);
expect(result).to.equal(1); // Pool 2 has not reached maxStake
})

it('should throw an error if both pools have reached maxStake', () => {
expect(() => TonPoolStaker.selectPool(200n, 1000n, [1000n, 1000n])).to.throw("Both pools have reached their maximum stake limits");
})
})

0 comments on commit 2f4bb68

Please sign in to comment.