diff --git a/README.md b/README.md index b01bddef..211581ee 100644 --- a/README.md +++ b/README.md @@ -60,13 +60,12 @@ You can use each submodule individually. Click the module below to get more deta * ✅ Get block transactions * ✅ Get account transactions * ✅ Deploy contracts and send external messages using Tonlib -* ✅ Wallet operations (Simple (V1), V2, V3, V4 (plugins), ~~Lockup~~, DNS, Jetton, NFT) +* ✅ Wallet operations (Simple (V1), V2, V3, V4 (plugins), ~~Lockup~~, DNS, Jetton, NFT, Payment-channels) * ✅ HashMap, HashMapE, PfxHashMap and PfxHashMapE serialization / deserialization ### Todo * Re-test lockup (restricted) wallet * Support tuple and list as arguments for runMethod -* Add TON Payment channel support * Add highload wallet support * Fix dictionary de/serialization with one entry * Improve test coverage and add more integration tests diff --git a/smartcontract/README.md b/smartcontract/README.md index 9e0f1143..fbe45864 100644 --- a/smartcontract/README.md +++ b/smartcontract/README.md @@ -41,6 +41,7 @@ Currently, following wallet versions and revisions are supported: * Dns [(see usage example)](dns-example.md) * Jetton [(see usage example)](jetton-example.md) * NFT [(see usage example)](nft-example.md) +* Payment channels [(see usage example)](./src/test/java/org/ton/java/smartcontract/integrationtests/TestPayments.java) * Custom contract [(see usage example)](custom-smc-example.md) diff --git a/smartcontract/src/main/java/org/ton/java/smartcontract/payments/FromWallet.java b/smartcontract/src/main/java/org/ton/java/smartcontract/payments/FromWallet.java index c68e89d5..901f100d 100644 --- a/smartcontract/src/main/java/org/ton/java/smartcontract/payments/FromWallet.java +++ b/smartcontract/src/main/java/org/ton/java/smartcontract/payments/FromWallet.java @@ -7,13 +7,11 @@ import org.ton.java.smartcontract.wallet.Options; import org.ton.java.smartcontract.wallet.WalletContract; import org.ton.java.tonlib.Tonlib; -import org.ton.java.utils.Utils; import java.math.BigInteger; import static java.util.Objects.nonNull; - public class FromWallet extends PaymentChannel { Options options; @@ -33,26 +31,22 @@ public FromWallet(Tonlib tonlib, WalletContract wallet, byte[] secretKey, Option } public FromWallet deploy(BigInteger amount) { - Utils.sleep(1, "deploying"); transfer(null, true, amount); return this; } public FromWallet topUp(BigInteger balanceA, BigInteger balanceB, BigInteger amount) { - Utils.sleep(1, "topup"); transfer(this.createTopUpBalance(balanceA, balanceB), amount); return this; } public FromWallet init(BigInteger balanceA, BigInteger balanceB, BigInteger amount) { - Utils.sleep(1, "init"); transfer(this.createInitChannel(balanceA, balanceB).getCell(), amount); return this; } - public FromWallet send() { // todo estimateFee - Utils.sleep(1, "sending"); + public FromWallet send() { if (nonNull(extMsg)) { tonlib.sendRawMessage(extMsg.message.toBocBase64(false)); } else { @@ -61,6 +55,15 @@ public FromWallet send() { // todo estimateFee return this; } + public FromWallet estimateFee() { + if (nonNull(extMsg)) { + tonlib.estimateFees(extMsg.address.toString(), extMsg.message.toBocBase64(false), extMsg.code.toBocBase64(false), extMsg.data.toBocBase64(false), false); + } else { + throw new Error("cannot send empty external message"); + } + return this; + } + public FromWallet close(ChannelState channelState, byte[] hisSignature, BigInteger amount) { transfer(this.createCooperativeCloseChannel(hisSignature, channelState).getCell(), amount); return this; diff --git a/smartcontract/src/test/java/org/ton/java/smartcontract/integrationtests/TestPayments.java b/smartcontract/src/test/java/org/ton/java/smartcontract/integrationtests/TestPayments.java index 869e0bc4..65549e27 100644 --- a/smartcontract/src/test/java/org/ton/java/smartcontract/integrationtests/TestPayments.java +++ b/smartcontract/src/test/java/org/ton/java/smartcontract/integrationtests/TestPayments.java @@ -39,8 +39,8 @@ public static void setUpClass() throws InterruptedException { // The payment channel is established between two participants A and B. // Each has own secret key, which he does not reveal to the other. - walletA = GenerateWallet.random(tonlib, 7); - walletB = GenerateWallet.random(tonlib, 7); + walletA = GenerateWallet.random(tonlib, 3); + walletB = GenerateWallet.random(tonlib, 3); walletAddressA = walletA.getWallet().getAddress(); walletAddressB = walletB.getWallet().getAddress(); } @@ -100,37 +100,6 @@ public void testPayments() { assertThat(channelA.getAddress().toString(true, true, true)).isEqualTo(channelB.getAddress().toString(true, true, true)); - // Interaction with the smart contract of the payment channel is carried out by sending messages from the wallet to it. - // So let's create helpers for such sends. - -// InitExternalMessage msg = channelB.createInitExternalMessage(walletA.getKeyPair().getSecretKey()); -// log.info("in {}", msg); -// BigInteger balance = TestFaucet.topUpContract(tonlib, Address.of(msg.address.toString(true, true, false)), Utils.toNano(1)); -// log.info("balance {}", Utils.formatNanoValue(balance)); -// tonlib.sendRawMessage(msg.message.toBocBase64(false)); -// Cannot run message on account: inbound external message rejected by transaction CDE825A804357B8E9390941832574F35A5DE8750AD6FC034A4041F7A953197F9: -// exitcode=65535, steps=54, gas_used=0 -// VM Log (truncated): -//... PUSHCONT x30F017DB31 -// execute IFJMP -// execute NIP -// execute PUSHINT 625158801 -// execute EQUAL -// execute PUSHCONT xF018DB31 -// execute IFJMP -// execute implicit JMPREF -// execute PUSHPOW2DEC 16 -// execute THROWANY -// default exception handler, terminating vm with exit code 65535 - - -// channelA.transfer(walletA.getWallet(), walletA.getKeyPair().getSecretKey(), tonlib, null, true, Utils.toNano(0.05)); -// channelA.send(tonlib); - // liteServer_sendMsgStatus = 1 , OK - // msg does not come to chanelA contract at all - // Bounced op=no-op from channel-smc - - FromWallet fromWalletA = channelA.fromWallet(tonlib, walletA.getWallet(), walletA.getKeyPair().getSecretKey()); FromWallet fromWalletB = channelB.fromWallet(tonlib, walletB.getWallet(), walletB.getKeyPair().getSecretKey()); @@ -155,9 +124,10 @@ public void testPayments() { // Now each parties must send their initial balance from the wallet to the channel contract. fromWalletA.topUp(channelInitState.getBalanceA(), BigInteger.ZERO, channelInitState.getBalanceA().add(Utils.toNano(0.05))).send(); // +0.05 TON to network fees + Utils.sleep(25, "topping up from wallet A..."); fromWalletB.topUp(BigInteger.ZERO, channelInitState.getBalanceB(), channelInitState.getBalanceB().add(Utils.toNano(0.05))).send(); // +0.05 TON to network fees + Utils.sleep(25, "topping up from wallet B..."); - Utils.sleep(25, "topping up..."); log.info("channel A state {}", channelA.getChannelState(tonlib)); log.info("channel A data {}", channelA.getData(tonlib)); diff --git a/smartcontract/src/test/java/org/ton/java/smartcontract/integrationtests/TestWalletV1R3DeployTransfer.java b/smartcontract/src/test/java/org/ton/java/smartcontract/integrationtests/TestWalletV1R3DeployTransfer.java index fa4010b9..7425a5ef 100644 --- a/smartcontract/src/test/java/org/ton/java/smartcontract/integrationtests/TestWalletV1R3DeployTransfer.java +++ b/smartcontract/src/test/java/org/ton/java/smartcontract/integrationtests/TestWalletV1R3DeployTransfer.java @@ -15,6 +15,7 @@ import org.ton.java.smartcontract.wallet.v1.SimpleWalletContractR3; import org.ton.java.tonlib.Tonlib; import org.ton.java.tonlib.types.AccountState; +import org.ton.java.tonlib.types.QueryFees; import org.ton.java.utils.Utils; import java.math.BigInteger; @@ -89,4 +90,28 @@ public void testNewWalletSimple() throws InterruptedException { log.info("seqno {}", contract.getSeqno(tonlib)); log.info("pubkey {}", contract.getPublicKey(tonlib)); } + + @Test + public void testWalletSimpleEstimateFees() { + TweetNaclFast.Signature.KeyPair keyPair = Utils.generateSignatureKeyPair(); + + Options options = Options.builder() + .publicKey(keyPair.getPublicKey()) + .wc(0L) + .build(); + + Wallet wallet = new Wallet(WalletVersion.simpleR3, options); + SimpleWalletContractR3 contract = wallet.create(); + + InitExternalMessage msg = contract.createInitExternalMessage(keyPair.getSecretKey()); + + Tonlib tonlib = Tonlib.builder().testnet(true).build(); + + QueryFees feesWithCodeData = tonlib.estimateFees(msg.address.toString(), msg.message.toBocBase64(false), msg.code.toBocBase64(false), msg.data.toBocBase64(false), false); + log.info("fees {}", feesWithCodeData); + assertThat(feesWithCodeData).isNotNull(); + QueryFees feesBodyOnly = tonlib.estimateFees(msg.address.toString(), msg.message.toBocBase64(false), null, null, false); + log.info("fees {}", feesBodyOnly); + assertThat(feesBodyOnly).isNotNull(); + } }