Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] New generic interface for smartcontracts #26

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
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
Original file line number Diff line number Diff line change
Expand Up @@ -10,32 +10,43 @@ import org.ton.lite.api.liteserver.LiteServerSendMsgStatus
import org.ton.tlb.TlbCodec
import org.ton.tlb.storeTlb

/**
* Basic interface to any smart-contract that can be used to either deploy new contracts to the network or work with
* already deployed contracts.
* Only fields that are by definition known for all contracts are present here, more can be freely added by specific implementations
*/
interface SmartContract {
val liteApi: LiteApi
val workchainId: Int
val name: String
val code: Cell

/**
* Different workchains may define own mechanisms, therefore it is important to know workchain id.
*
* Note: This particular smart-contract interface is only valid for basic workchain (id=0) and masterchain (id=1)
*/
val workchain_id: Int

/**
* Create initial code cell
*/
fun createCodeInit(): Cell

/**
* Create initial data cell
*/
fun createDataInit(): Cell

fun createStateInit(): StateInit = StateInit(
code, createDataInit()
)

fun address(stateInit: StateInit = createStateInit()): AddrStd =
address(workchainId, stateInit)

fun createExternalInitMessage(): Message<Cell>
/**
* Create state_init structure based on [createCodeInit] and [createDataInit]
*/
fun createStateInit(): StateInit = StateInit(createCodeInit(), createDataInit())

suspend fun deploy(): LiteServerSendMsgStatus

override fun toString(): String
/**
* Compute address of a smart-contract based on its [createStateInit]
*/
fun address(): AddrStd = address(workchain_id, createStateInit())

companion object {
private val stateInitCodec: TlbCodec<StateInit> by lazy {
StateInit.tlbCodec()
}
private val stateInitCodec: TlbCodec<StateInit> by lazy { StateInit.tlbCodec() }

/** Compute address of a smart-contract by its [workchainId] and [stateInit] */
@JvmStatic
fun address(workchainId: Int, stateInit: StateInit): AddrStd {
val cell = CellBuilder.createCell {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package org.ton.smartcontract.wallet

import org.ton.api.pk.PrivateKeyEd25519
import org.ton.api.pub.PublicKeyEd25519
import org.ton.cell.Cell
import org.ton.cell.CellBuilder
import org.ton.smartcontract.wallet.builder.SignedSeqnoTransferBuilder

abstract class BasicTransferWallet<TMB : SignedSeqnoTransferBuilder>(
private val private_key: PrivateKeyEd25519,
override val workchain_id: Int = 0
) : SignedSeqnoTransferWallet<TMB> {
override fun publicKey(): PublicKeyEd25519 = private_key.publicKey()

override fun createDataInit(): Cell = CellBuilder.createCell {
storeUInt(0, 32) // seqno
storeBytes(publicKey().key)
}
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package org.ton.smartcontract.wallet

import org.ton.smartcontract.wallet.builder.SeqnoTransferBuilder

/**
* A wallet which requires you to supply seqno in order to
*/
interface SeqnoTransferWallet<TMB : SeqnoTransferBuilder> : TransferWallet<TMB>,
SeqnoWallet
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,27 @@ package org.ton.smartcontract.wallet

import org.ton.api.tonnode.TonNodeBlockIdExt
import org.ton.block.VmStackValue
import org.ton.lite.api.LiteApi
import org.ton.lite.api.liteserver.LiteServerAccountId
import org.ton.smartcontract.SmartContract

interface SeqnoWallet : SmartContract {
suspend fun seqno(): Int = seqno(liteApi.getMasterchainInfo().last)
/**
* Most wallets implement sequence number (seqno) in order to circumvent replay attacks
*/
interface SeqnoWallet : Wallet {
/**
* Get most recent seqno
*/
suspend fun seqno(liteApi: LiteApi): Int = seqno(liteApi, liteApi.getMasterchainInfo().last)

suspend fun seqno(blockIdExt: TonNodeBlockIdExt): Int {
/**
* Get seqno value as of [referenceBlockId]
*/
suspend fun seqno(liteApi: LiteApi, referenceBlockId: TonNodeBlockIdExt): Int {
val liteServerAccountId = LiteServerAccountId(address())
val result = liteApi.runSmcMethod(4, blockIdExt, liteServerAccountId, "seqno")
val result = liteApi.runSmcMethod(4, referenceBlockId, liteServerAccountId, "seqno")
require(result.exitCode == 0) { "failed to run smc method `seqno` with exit code ${result.exitCode}" }
require(result.resultValues().orEmpty().size == 1 && (result.first() !is VmStackValue.TinyInt))
{ "failed to get proper result for `seqno` smc method" }
return (result.first() as VmStackValue.TinyInt).value.toInt()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package org.ton.smartcontract.wallet

import org.ton.smartcontract.wallet.builder.SignedSeqnoTransferBuilder

/**
* Base class for almost every wallet contract out there, uses private key for authorization but also requires seqno
*/
interface SignedSeqnoTransferWallet<TMB : SignedSeqnoTransferBuilder> : SignedTransferWallet<TMB>,
SeqnoTransferWallet<TMB>
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package org.ton.smartcontract.wallet

import org.ton.api.pub.PublicKeyEd25519
import org.ton.smartcontract.wallet.builder.SignedTransferBuilder

/**
* Most often operations on wallets are authorized using a single Ed25519 private key
*/
interface SignedTransferWallet<TMB : SignedTransferBuilder> : TransferWallet<TMB> {
/**
* Get wallet's public key
*/
fun publicKey(): PublicKeyEd25519
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package org.ton.smartcontract.wallet

import org.ton.api.pub.PublicKeyEd25519
import org.ton.api.tonnode.TonNodeBlockIdExt
import org.ton.block.VmStackValue
import org.ton.lite.api.LiteApi
import org.ton.lite.api.liteserver.LiteServerAccountId

/**
* Most wallets implement method `get_public_key`
*/
interface SignedWallet : Wallet {
/**
* Get most recent public_key
*/
suspend fun getPublicKey(liteApi: LiteApi): PublicKeyEd25519 =
getPublicKey(liteApi, liteApi.getMasterchainInfo().last)

/**
* Get public key value as of [referenceBlockId]
*/
suspend fun getPublicKey(liteApi: LiteApi, referenceBlockId: TonNodeBlockIdExt): PublicKeyEd25519 {
val liteServerAccountId = LiteServerAccountId(address())
val result = liteApi.runSmcMethod(4, referenceBlockId, liteServerAccountId, "get_public_key")
require(result.exitCode == 0) { "failed to run smc method `get_public_key` with exit code ${result.exitCode}" }
require(result.resultValues().orEmpty().size == 1 && (result.first() !is VmStackValue.Int))
{ "failed to get proper result for `get_public_key` smc method" }
val rawPublicKey = (result.first() as VmStackValue.Int).value.toByteArray()
return PublicKeyEd25519(rawPublicKey)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package org.ton.smartcontract.wallet

import org.ton.lite.api.LiteApi
import org.ton.smartcontract.wallet.builder.TransferBuilder

/**
* Wallet contract we have a permission to perform operations on, such as transfers etc.
*/
interface TransferWallet<TMB : TransferBuilder> : Wallet {
suspend fun beginTransfer(lite_api: LiteApi): TMB

/**
* Perform a transfer of funds
*/
suspend fun transfer(lite_api: LiteApi, builder: TMB.() -> Unit) {
val msg = beginTransfer(lite_api).apply(builder).createMessage()
lite_api.sendMessage(msg)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package org.ton.smartcontract.wallet

import org.ton.cell.Cell
import org.ton.smartcontract.SmartContract

/**
* Base interface for all wallet contracts, any externally-controlled contract whose main purpose is sending and receiving funds.
*/
interface Wallet : SmartContract {
override fun createDataInit(): Cell {
throw Exception("cannot create data cell")
}
}

This file was deleted.

Loading