-
Notifications
You must be signed in to change notification settings - Fork 30
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #105 from h6x0r/wallet-v5-gasless
wallet v5 gasless transactions draft
- Loading branch information
Showing
2 changed files
with
210 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
205 changes: 205 additions & 0 deletions
205
...act/src/test/java/org/ton/java/smartcontract/integrationtests/GaslessTransactionTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,205 @@ | ||
package org.ton.java.smartcontract.integrationtests; | ||
|
||
import static org.assertj.core.api.AssertionsForClassTypes.assertThat; | ||
import static org.ton.java.utils.Utils.bytesToHex; | ||
|
||
import com.iwebpp.crypto.TweetNaclFast; | ||
import java.math.BigInteger; | ||
import java.util.ArrayList; | ||
import java.util.Arrays; | ||
import java.util.Collections; | ||
import lombok.extern.slf4j.Slf4j; | ||
import org.junit.Test; | ||
import org.ton.java.address.Address; | ||
import org.ton.java.cell.Cell; | ||
import org.ton.java.mnemonic.Mnemonic; | ||
import org.ton.java.mnemonic.Pair; | ||
import org.ton.java.smartcontract.token.ft.JettonMinterStableCoin; | ||
import org.ton.java.smartcontract.token.ft.JettonWalletStableCoin; | ||
import org.ton.java.smartcontract.types.Destination; | ||
import org.ton.java.smartcontract.types.HighloadQueryId; | ||
import org.ton.java.smartcontract.types.WalletV5Config; | ||
import org.ton.java.smartcontract.utils.MsgUtils; | ||
import org.ton.java.smartcontract.wallet.v5.WalletV5; | ||
import org.ton.java.tonlib.Tonlib; | ||
import org.ton.java.utils.Utils; | ||
import org.ton.schema.gasless.GaslessConfig; | ||
import org.ton.schema.gasless.SignRawMessage; | ||
import org.ton.schema.gasless.SignRawParams; | ||
import org.ton.tonapi.sync.Tonapi; | ||
|
||
import java.util.List; | ||
import org.ton.tonapi.sync.methods.GaslessMethod; | ||
|
||
|
||
@Slf4j | ||
public class GaslessTransactionTest { | ||
|
||
private static final String API_KEY = ""; | ||
|
||
private static final Address RECIPIENT = Address.of("UQB8ANV_ynITQr1qHXADHDKYUAQ9VFcCRDZB7h4aPuPKuFtm"); | ||
private static final Address USDT_MASTER = Address.of("EQCxE6mUtQJKFnGfaROTKOt1lZbDiiX1kCixRv7Nw2Id_sDs"); | ||
|
||
// TON API client initialization | ||
private final Tonapi tonApiClient = new Tonapi(API_KEY, true, 10); | ||
|
||
static Tonlib tonlib; | ||
|
||
private Address getRelayAddress(GaslessMethod gaslessMethod) { | ||
GaslessConfig config = gaslessMethod.getConfig(); | ||
return Address.of(config.getRelayAddress()); | ||
} | ||
|
||
@Test | ||
public void testGaslessTransaction() throws Exception { | ||
tonlib = Tonlib.builder() | ||
.testnet(false) | ||
.ignoreCache(false) | ||
.build(); | ||
|
||
GaslessMethod gaslessMethod = new GaslessMethod(tonApiClient); | ||
|
||
// Your seed phrase | ||
List<String> srcSeed = Arrays.asList(); | ||
|
||
// Keypair generated from seed phrase | ||
Pair keyPairSrc = Mnemonic.toKeyPair(srcSeed); | ||
|
||
log.info("public-key {}", Utils.bytesToHex(keyPairSrc.getPublicKey())); | ||
log.info("secret-key {}", Utils.bytesToHex(keyPairSrc.getSecretKey())); | ||
|
||
TweetNaclFast.Signature.KeyPair keyPairSigSrc = TweetNaclFast.Signature.keyPair_fromSeed(keyPairSrc.getSecretKey()); | ||
|
||
long walletId = 2147483409L; | ||
|
||
// connect to a source wallet | ||
WalletV5 srcWallet = WalletV5.builder() | ||
.tonlib(tonlib) | ||
.isSigAuthAllowed(true) | ||
.walletId(walletId) | ||
.keyPair(keyPairSigSrc) | ||
.build(); | ||
|
||
Address srcWalletAddress = srcWallet.getAddress(); | ||
|
||
String nonBounceableAddress = srcWalletAddress.toNonBounceable(); | ||
String bounceableAddress = srcWalletAddress.toBounceable(); | ||
log.info("non-bounceable address {}", nonBounceableAddress); | ||
log.info("bounceable address {}", bounceableAddress); | ||
log.info("pub-key {}", bytesToHex(srcWallet.getKeyPair().getPublicKey())); | ||
log.info("prv-key {}", bytesToHex(srcWallet.getKeyPair().getSecretKey())); | ||
log.info("Wallet {} balance: {}", srcWallet.getName(), Utils.formatNanoValue(srcWallet.getBalance())); | ||
|
||
log.info("walletId {}", srcWallet.getWalletId()); | ||
log.info("publicKey {}", bytesToHex(srcWallet.getPublicKey())); | ||
log.info("isSignatureAuthAllowed {}", srcWallet.getIsSignatureAuthAllowed()); | ||
|
||
String status = tonlib.getAccountStatus(Address.of(bounceableAddress)); | ||
log.info("account status {}", status); | ||
|
||
BigInteger balance = tonlib.getAccountBalance(Address.of(bounceableAddress)); | ||
log.info("account balance {}", Utils.formatNanoValue(balance)); | ||
|
||
Address relay = getRelayAddress(gaslessMethod); | ||
|
||
JettonMinterStableCoin usdtMasterWallet = JettonMinterStableCoin.builder() | ||
.tonlib(tonlib) | ||
.customAddress(USDT_MASTER) | ||
.build(); | ||
|
||
log.info("USDT total supply: {}", Utils.formatJettonValue(usdtMasterWallet.getTotalSupply(), 6, 2)); | ||
|
||
// get my JettonWallet the one that holds my jettons (USDT) tokens | ||
JettonWalletStableCoin srcJettonWallet = usdtMasterWallet.getJettonWallet(srcWalletAddress); | ||
log.info("my jettonWallet balance: {}", Utils.formatJettonValue(srcJettonWallet.getBalance(), 6, 2)); | ||
|
||
Cell bodyCell = srcWallet.createBulkTransfer( | ||
Collections.singletonList( | ||
Destination.builder() | ||
.address(RECIPIENT.toString()) | ||
.bounce(true) | ||
.body(JettonWalletStableCoin.createTransferBody( | ||
0, | ||
BigInteger.valueOf(1_000_000), // 1 USDT | ||
RECIPIENT, // recipient | ||
relay, // response address | ||
BigInteger.ONE, // forward amount | ||
MsgUtils.createTextMessageBody("test gasless") // comment | ||
)) | ||
.build())) | ||
.toCell(); | ||
|
||
WalletV5Config walletV5Config = WalletV5Config.builder() | ||
.walletId(walletId) | ||
.seqno(srcWallet.getSeqno()) | ||
.body(bodyCell) | ||
.build(); | ||
|
||
String msg = srcWallet.createInternalTransferBody(walletV5Config).toHex(); | ||
|
||
log.info("Prepared message: {}", msg); | ||
|
||
String pubKey = bytesToHex(keyPairSigSrc.getPublicKey()); | ||
|
||
// we send a single message containing a transfer from our wallet to a desired destination. | ||
// as a result of estimation, TonAPI returns a list of messages that we need to sign. | ||
// the first message is a fee transfer to the relay address, the second message is our original transfer. | ||
SignRawParams signRawParams = gaslessMethod.estimateGasPrice( | ||
USDT_MASTER.toString(), | ||
srcWalletAddress.toString(), | ||
pubKey, | ||
Arrays.asList(msg) | ||
); | ||
|
||
// signRawParams is the same structure as signRawParams in tonconnect. | ||
List<Destination> msgs = new ArrayList<>(); | ||
|
||
for (SignRawMessage signRawMessage : signRawParams.getMessages()) { | ||
|
||
// InternalMessageInfo internalMessageInfo = InternalMessageInfo.builder() | ||
// .iHRDisabled(true) | ||
// .bounce(false) | ||
// .bounced(false) | ||
// .value(CurrencyCollection.builder() | ||
// .coins(Utils.toNano(1)) | ||
// .build()) | ||
// .srcAddr(srcWallet.getAddressIntStd()) | ||
// .dstAddr(MsgAddressIntStd.builder() | ||
// .workchainId(RECIPIENT.wc) | ||
// .address(RECIPIENT.toBigInteger()) | ||
// .build()) | ||
// .build(); | ||
|
||
|
||
Cell msgCell = Cell.fromBoc(signRawMessage.getPayload()); | ||
BigInteger msgAmount = signRawMessage.getAmount(); | ||
Destination rawMessage = Destination.builder() | ||
.address(signRawMessage.getAddress()) | ||
.amount(msgAmount) | ||
.body(msgCell) | ||
.build(); | ||
msgs.add(rawMessage); | ||
} | ||
|
||
WalletV5Config conf = WalletV5Config.builder() | ||
.walletId(42) | ||
.queryId(HighloadQueryId.fromSeqno(1).getQueryId()) | ||
.body(srcWallet.createBulkTransfer(msgs).toCell()) | ||
.build(); | ||
|
||
Cell message = srcWallet.prepareExternalMsg(conf).toCell(); | ||
String msgBoc = message.toBase64(); | ||
|
||
boolean isTxSuccess = gaslessMethod.send(pubKey, msgBoc); | ||
|
||
assertThat(isTxSuccess).isEqualTo(true); | ||
|
||
BigInteger balanceOfDestinationWallet = tonlib.getAccountBalance(RECIPIENT); | ||
log.info("balanceOfDestinationWallet in toncoins: {}", balanceOfDestinationWallet); | ||
|
||
JettonWalletStableCoin dstJettonWallet = usdtMasterWallet.getJettonWallet(RECIPIENT); | ||
BigInteger dstJettonWalletBalance = dstJettonWallet.getBalance(); | ||
log.info("dstJettonWallet balance in jettons: {}", Utils.formatJettonValue(dstJettonWalletBalance, 6, 2)); | ||
|
||
} | ||
} |