Skip to content

Commit

Permalink
0.4.3
Browse files Browse the repository at this point in the history
  • Loading branch information
andreypfau committed Feb 14, 2025
1 parent b306a73 commit 4086b55
Show file tree
Hide file tree
Showing 17 changed files with 885 additions and 22 deletions.
1 change: 1 addition & 0 deletions bitstring/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ kotlin {
dependencies {
api(projects.tonKotlinCrypto)
implementation(libs.serialization.core)
implementation(libs.kotlinx.io)
}
}
}
Expand Down
3 changes: 3 additions & 0 deletions bitstring/src/BitString.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

package org.ton.bitstring

import kotlinx.io.bytestring.ByteString
import kotlinx.serialization.Serializable
import kotlin.contracts.ExperimentalContracts
import kotlin.contracts.contract
Expand All @@ -19,6 +20,8 @@ public inline fun BitString(hex: String): BitString = BitString.parse(hex)
public inline fun Iterable<Boolean>.toBitString(): BitString = BitString(this)
public inline fun BooleanArray.toBitString(): BitString = BitString(*this)
public inline fun ByteArray.toBitString(size: Int = this.size * Byte.SIZE_BITS): BitString = BitString(this, size)
public inline fun ByteString.toBitString(size: Int = this.size * Byte.SIZE_BITS): BitString =
BitString(this.toByteArray(), size)

@Serializable(with = FiftHexBitStringSerializer::class)
public interface BitString : Iterable<Boolean>, Comparable<BitString> {
Expand Down
7 changes: 4 additions & 3 deletions bitstring/src/ByteBackedBitString.kt
Original file line number Diff line number Diff line change
Expand Up @@ -45,14 +45,15 @@ public open class ByteBackedBitString protected constructor(
if (augment && (size % 8 != 0)) {
appendAugmentTag(bytes, size)
} else {
bytes.copyOf()
bytes.copyOf((size + 7) ushr 3)
}

override fun toBooleanArray(): BooleanArray = toList().toBooleanArray()

override fun toMutableBitString(): MutableBitString = ByteBackedMutableBitString.of(bytes.copyOf(), size)
override fun toMutableBitString(): MutableBitString =
ByteBackedMutableBitString.of(bytes.copyOf((size + 7) ushr 3), size)

override fun toBitString(): BitString = ByteBackedBitString(size, bytes.copyOf())
override fun toBitString(): BitString = ByteBackedBitString(size, bytes.copyOf((size + 7) ushr 3))

override fun iterator(): Iterator<Boolean> = BitStringIterator(this)

Expand Down
8 changes: 8 additions & 0 deletions block-tlb/src/AddrStd.kt
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package org.ton.block

import kotlinx.io.bytestring.ByteString
import kotlinx.serialization.SerialName
import org.ton.bitstring.BitString
import org.ton.bitstring.toBitString
Expand Down Expand Up @@ -33,6 +34,7 @@ public data class AddrStd(
public constructor() : this(0, BitString(256))
public constructor(workchainId: Int, address: BitString) : this(null, workchainId, address)
public constructor(workchainId: Int, address: ByteArray) : this(null, workchainId, address)
public constructor(workchainId: Int, address: ByteString) : this(null, workchainId, address.toByteArray())
public constructor(anycast: Anycast?, workchainId: Int, address: ByteArray) : this(
anycast.toMaybe(),
workchainId,
Expand All @@ -45,6 +47,12 @@ public data class AddrStd(
address.toBitString()
)

public constructor(anycast: Anycast?, workchainId: Int, address: ByteString) : this(
anycast.toMaybe(),
workchainId,
address.toBitString()
)

init {
require(address.size == 256) { "expected address.size == 256, actual: ${address.size}" }
}
Expand Down
38 changes: 24 additions & 14 deletions block-tlb/src/StorageUsedShort.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,24 +3,34 @@ package org.ton.block
import org.ton.cell.CellBuilder
import org.ton.cell.CellSlice
import org.ton.cell.invoke
import org.ton.tlb.*
import org.ton.kotlin.cell.CellSize
import org.ton.tlb.TlbConstructor
import org.ton.tlb.loadTlb
import org.ton.tlb.providers.TlbConstructorProvider
import org.ton.tlb.storeTlb

/**
* Cell tree storage stats.
*/
public data class StorageUsedShort(
val cells: Long,
val bits: Long
) : TlbObject {
override fun print(printer: TlbPrettyPrinter): TlbPrettyPrinter {
return printer {
type("storage_used_short") {
field("cells", cells)
field("bits", bits)
}
}
/**
* Total number of cells in tree.
*/
val cellCount: Long,

/**
* Total number of bits in tree.
*/
val bitCount: Long
) {
public constructor(cellSize: CellSize) : this(cellSize.minRefs.toLong(), cellSize.minBits.toLong()) {
require(cellSize.isFixed()) { "Cell size must be fixed" }
}

override fun toString(): String = print().toString()
public fun toCellSize(): CellSize = CellSize(bitCount.toInt(), cellCount.toInt())

public operator fun plus(other: StorageUsedShort): StorageUsedShort =
StorageUsedShort(cellCount + other.cellCount, bitCount + other.bitCount)

public companion object : TlbConstructorProvider<StorageUsedShort> by StorageUsedShortTlbConstructor {
public val ZERO: StorageUsedShort = StorageUsedShort(0, 0)
Expand All @@ -36,8 +46,8 @@ private object StorageUsedShortTlbConstructor : TlbConstructor<StorageUsedShort>
override fun storeTlb(
cellBuilder: CellBuilder, value: StorageUsedShort
) = cellBuilder {
storeTlb(varUInteger7Codec, VarUInteger(value.cells))
storeTlb(varUInteger7Codec, VarUInteger(value.bits))
storeTlb(varUInteger7Codec, VarUInteger(value.cellCount))
storeTlb(varUInteger7Codec, VarUInteger(value.bitCount))
}

override fun loadTlb(
Expand Down
2 changes: 2 additions & 0 deletions block-tlb/src/account/Account.kt
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import org.ton.tlb.TlbCodec

/**
* Existing account data.
*
* @see [ShardAccount]
*/
public data class Account(
/**
Expand Down
54 changes: 54 additions & 0 deletions block-tlb/src/config/BurningConfig.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
@file:Suppress("PackageDirectoryMismatch")

package org.ton.kotlin.config

import org.ton.bigint.div
import org.ton.bigint.times
import org.ton.bigint.toBigInt
import org.ton.block.AddrStd
import org.ton.block.Coins
import org.ton.cell.CellBuilder
import org.ton.cell.CellSlice
import org.ton.kotlin.cell.CellContext
import org.ton.tlb.TlbCodec

public data class BurningConfig(
val blackholeAddress: AddrStd?,
val feeBurnNum: Int,
val feeBurnDenom: Int
) {
init {
require(feeBurnDenom >= 1) { "feeBurnDenom must be at least 1, actual: $feeBurnDenom" }
require(feeBurnNum in 0..feeBurnDenom) { "feeBurnNum must be in 0..$feeBurnDenom, actual: $feeBurnNum" }
}

public fun calculateBurnedFees(value: Coins): Coins {
if (value == Coins.ZERO) return value
return Coins(value.amount.value.times(feeBurnNum.toBigInt()).div(feeBurnDenom.toBigInt()))
}

public companion object : TlbCodec<BurningConfig> by BurningConfigTlbCodec
}

private object BurningConfigTlbCodec : TlbCodec<BurningConfig> {
override fun loadTlb(slice: CellSlice, context: CellContext): BurningConfig {
val tag = slice.loadUInt(8).toInt()
require(tag == 0x01) { "Invalid BurningConfig tag: ${tag.toHexString()}" }
val blackholeAddress = if (slice.loadBoolean()) AddrStd(-1, slice.loadByteArray(32)) else null
val feeBurnNum = slice.loadUInt(32).toInt()
val feeBurnDenom = slice.loadUInt(32).toInt()
return BurningConfig(blackholeAddress, feeBurnNum, feeBurnDenom)
}

override fun storeTlb(builder: CellBuilder, value: BurningConfig) {
builder.storeUInt(0x01, 8)
if (value.blackholeAddress != null) {
builder.storeBoolean(true)
builder.storeBitString(value.blackholeAddress.address)
} else {
builder.storeBoolean(false)
}
builder.storeUInt(value.feeBurnNum, 32)
builder.storeUInt(value.feeBurnDenom, 32)
}
}
89 changes: 89 additions & 0 deletions block-tlb/src/config/StoragePrices.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
package org.ton.block.config

import org.ton.bigint.*
import org.ton.block.Coins
import org.ton.block.StorageUsedShort
import org.ton.cell.CellBuilder
import org.ton.cell.CellSlice
import org.ton.kotlin.cell.CellContext
import org.ton.tlb.TlbCodec

/**
* Storage prices for some interval.
*/
public data class StoragePrices(
/**
* Unix timestamp in seconds since which this prices are used.
*/
val validSince: Long,

/**
* Bit price in base workchain.
*/
val bitPrice: Long,

/**
* Cell price in base workchain.
*/
val cellPrice: Long,

/**
* Bit price in masterchain.
*/
val mcBitPrice: Long,

/**
* Cell price in masterchain.
*/
val mcCellPrice: Long,
) {
/**
* Computes the amount of fees for storing [stats] data for [delta] seconds.
*/
public fun computeStorageFee(
isMasterchain: Boolean,
delta: Long,
stats: StorageUsedShort
): Coins {
var result = if (isMasterchain) {
(stats.cellCount.toBigInt().times(mcCellPrice.toBigInt())).plus(
(stats.bitCount.toBigInt().times(mcBitPrice.toBigInt()))
)
} else {
(stats.cellCount.toBigInt().times(cellPrice.toBigInt())).plus(
(stats.bitCount.toBigInt().times(bitPrice.toBigInt()))
)
}
result = result.times(delta.toBigInt())
val r = result.and(0xFFFF.toBigInt()).toLong() != 0L
result = result.shr(16)
if (r) {
result = result.plus(1.toBigInt())
}
return Coins(result)
}

public companion object : TlbCodec<StoragePrices> by StoragePricesTlbCodec
}

private object StoragePricesTlbCodec : TlbCodec<StoragePrices> {
override fun loadTlb(slice: CellSlice, context: CellContext): StoragePrices {
val tag = slice.loadUInt(8).toInt()
require(tag == 0xCC) { "Invalid StorageUsedShort tag: ${tag.toHexString()}" }
val validSince = slice.loadUInt(32).toLong()
val bitPrice = slice.loadULong().toLong()
val cellPrice = slice.loadULong().toLong()
val mcBitPrice = slice.loadULong().toLong()
val mcCellPrice = slice.loadULong().toLong()
return StoragePrices(validSince, bitPrice, cellPrice, mcBitPrice, mcCellPrice)
}

override fun storeTlb(builder: CellBuilder, value: StoragePrices, context: CellContext) {
builder.storeUInt(0xCC, 8)
builder.storeUInt(value.validSince, 32)
builder.storeULong(value.bitPrice.toULong(), 64)
builder.storeULong(value.cellPrice.toULong(), 64)
builder.storeULong(value.mcBitPrice.toULong(), 64)
builder.storeULong(value.mcCellPrice.toULong(), 64)
}
}
2 changes: 1 addition & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ plugins {

allprojects {
group = "org.ton"
version = "0.4.2"
version = "0.4.3"

repositories {
mavenCentral()
Expand Down
2 changes: 1 addition & 1 deletion contract/src/wallet/WalletV4R2Contract.kt
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ public class WalletV4R2Contract(
walletId,
privateKey.publicKey(),
)
).value else null
).load() else null
val message = transferMessage(
address = address,
stateInit = stateInit,
Expand Down
24 changes: 24 additions & 0 deletions examples/src/ConfigContractExample.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package org.ton.kotlin.examples

import org.ton.block.AccountActive
import org.ton.kotlin.examples.contract.config.ConfigContract
import org.ton.kotlin.examples.contract.config.ConfigData
import org.ton.kotlin.examples.provider.LiteClientProvider
import org.ton.kotlin.examples.provider.liteClientMainnet

private val provider = LiteClientProvider(liteClientMainnet())

suspend fun main() {
val configContract = ConfigContract(provider)

val account = configContract.getState()?.loadAccount() ?: error("cant load account not found")
val data = (account.state as AccountActive).value.data.value?.cell ?: error("cant load data")
val configData = ConfigData.loadTlb(data.beginParse())
println("seqno: ${configData.seqno}")
println("public key: ${configData.publicKey}")
println("voteDict: ${configData.voteDict}")
for (entry in configData.voteDict) {
println("proposal: ${entry.key}")
println(entry.value)
}
}
31 changes: 31 additions & 0 deletions examples/src/WalletV4R2Example.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package org.ton.kotlin.examples

import org.ton.api.pk.PrivateKeyEd25519
import org.ton.block.AddrStd
import org.ton.block.Coins
import org.ton.block.CurrencyCollection
import org.ton.contract.wallet.WalletTransferBuilder
import org.ton.contract.wallet.WalletV4R2Contract
import org.ton.kotlin.account.balance
import org.ton.kotlin.examples.faucet.TestnetFaucet
import org.ton.kotlin.examples.provider.LiteClientProvider
import org.ton.kotlin.examples.provider.liteClientTestnet
import kotlin.random.Random

private val provider = LiteClientProvider(liteClientTestnet())

suspend fun main() {
val key = PrivateKeyEd25519(Random(42))
val wallet = WalletV4R2Contract(provider.liteClient, WalletV4R2Contract.address(key))
if (provider.getAccount(wallet.address)?.loadAccount().balance == CurrencyCollection.ZERO) {
TestnetFaucet(provider).topUpContract(wallet.address, Coins.of(1))
}
val transfer = WalletTransferBuilder().apply {
destination = AddrStd("0QCaLIqf03-qGREcf-Novb6H3UOXmIB1cLXxAJDxqrwe3rbP")
currencyCollection = CurrencyCollection(Coins.ofNano(1))
bounceable = false
}

val res = wallet.transfer(key, transfer.build())
println(res)
}
Loading

0 comments on commit 4086b55

Please sign in to comment.