|
| 1 | +using System.Globalization; |
| 2 | +using System.Numerics; |
| 3 | +using TonLibDotNet.Cells; |
| 4 | +using TonLibDotNet.Types.Msg; |
| 5 | +using TonLibDotNet.Types; |
| 6 | +using TonLibDotNet.Types.Smc; |
| 7 | +using TonLibDotNet.Utils; |
| 8 | + |
| 9 | +namespace TonLibDotNet.Recipes |
| 10 | +{ |
| 11 | + /// <remarks> |
| 12 | + /// Based on <see href="https://github.com/ton-blockchain/TEPs/blob/master/text/0074-jettons-standard.md">TEP 74: Fungible tokens (Jettons) standard</see> |
| 13 | + /// and <see href="https://github.com/ton-blockchain/token-contract/">Tokens Smart Contracts</see>. |
| 14 | + /// </remarks> |
| 15 | + public partial class Tep74Recipes |
| 16 | + { |
| 17 | + public decimal DefaultAmount { get; set; } = 0.1M; |
| 18 | + |
| 19 | + public int DefaultSendMode { get; set; } = 1; |
| 20 | + |
| 21 | + /// <summary> |
| 22 | + /// From <see href="https://github.com/ton-blockchain/token-contract/blob/main/ft/op-codes.fc">op-codes.fc</see> |
| 23 | + /// </summary> |
| 24 | + private const int OPTransfer = 0xf8a7ea5; |
| 25 | + |
| 26 | + /// <summary> |
| 27 | + /// From <see href="https://github.com/ton-blockchain/token-contract/blob/main/ft/op-codes.fc">op-codes.fc</see> |
| 28 | + /// </summary> |
| 29 | + private const int OPBurn = 0x595f07bc; |
| 30 | + |
| 31 | + public static readonly Tep74Recipes Instance = new(); |
| 32 | + |
| 33 | + /// <summary> |
| 34 | + /// Executes 'get_wallet_address' method on Jetton Minter contract, returns jetton address for specified owner (user) wallet address. |
| 35 | + /// </summary> |
| 36 | + /// <param name="tonClient"><see cref="ITonClient"/> instance.</param> |
| 37 | + /// <param name="jettonMinterAddress">Jetton Minter contract address.</param> |
| 38 | + /// <param name="ownerAddress">Owner (user) wallet address to get jetton address for.</param> |
| 39 | + /// <returns>Jetton address for specified Jetton and specified owner (user) wallet address.</returns> |
| 40 | + /// <exception cref="TonLibNonZeroExitCodeException" /> |
| 41 | + /// <seealso href="https://github.com/ton-blockchain/token-contract/blob/main/ft/jetton-minter.fc#L114">Source of 'get_wallet_address' method.</seealso> |
| 42 | + public async Task<string> GetWalletAddress(ITonClient tonClient, string jettonMinterAddress, string ownerAddress) |
| 43 | + { |
| 44 | + await tonClient.InitIfNeeded().ConfigureAwait(false); |
| 45 | + |
| 46 | + var smc = await tonClient.SmcLoad(new Types.AccountAddress(jettonMinterAddress)).ConfigureAwait(false); |
| 47 | + |
| 48 | + // slice get_wallet_address(slice owner_address) |
| 49 | + var stack = new List<Types.Tvm.StackEntry> |
| 50 | + { |
| 51 | + new Types.Tvm.StackEntrySlice(new Types.Tvm.Slice(new Cells.Boc(new Cells.CellBuilder().StoreAddressIntStd(ownerAddress).Build()).SerializeToBase64())), |
| 52 | + }; |
| 53 | + |
| 54 | + var result = await tonClient.SmcRunGetMethod(smc.Id, new MethodIdName("get_wallet_address"), stack).ConfigureAwait(false); |
| 55 | + |
| 56 | + await tonClient.SmcForget(smc.Id).ConfigureAwait(false); |
| 57 | + |
| 58 | + TonLibNonZeroExitCodeException.ThrowIfNonZero(result.ExitCode); |
| 59 | + |
| 60 | + return result.Stack[0].ToTvmCell().ToBoc().RootCells[0].BeginRead().LoadAddressIntStd(); |
| 61 | + } |
| 62 | + |
| 63 | + /// <summary> |
| 64 | + /// Executes 'get_wallet_data' method on Jetton address contract, returns information about this jetton address. |
| 65 | + /// </summary> |
| 66 | + /// <param name="tonClient"><see cref="ITonClient"/> instance.</param> |
| 67 | + /// <param name="jettonAddress">Jetton address to obtain info for.</param> |
| 68 | + /// <returns>Information about specified Jetton address: balance, owner (user) wallet address, Jetton Minter contract address.</returns> |
| 69 | + /// <remarks>Jetton contract must be deployed and active (to execute get-method).</remarks> |
| 70 | + /// <exception cref="TonLibNonZeroExitCodeException" /> |
| 71 | + /// <seealso href="https://github.com/ton-blockchain/token-contract/blob/main/ft/jetton-wallet.fc#L246">Source of 'get_wallet_address' method.</seealso> |
| 72 | + public async Task<(BigInteger balance, string ownerAddress, string jettonMinterAddress)> GetJettonAddressInfo(ITonClient tonClient, string jettonAddress) |
| 73 | + { |
| 74 | + await tonClient.InitIfNeeded().ConfigureAwait(false); |
| 75 | + |
| 76 | + var smc = await tonClient.SmcLoad(new Types.AccountAddress(jettonAddress)).ConfigureAwait(false); |
| 77 | + |
| 78 | + // (int balance:Coins, slice owner_address:MsgAddressInt, slice jetton_master_address:MsgAddressInt, cell jetton_wallet_code:^Cell) |
| 79 | + var result = await tonClient.SmcRunGetMethod(smc.Id, new MethodIdName("get_wallet_data")).ConfigureAwait(false); |
| 80 | + |
| 81 | + await tonClient.SmcForget(smc.Id).ConfigureAwait(false); |
| 82 | + |
| 83 | + TonLibNonZeroExitCodeException.ThrowIfNonZero(result.ExitCode); |
| 84 | + |
| 85 | + var balance = BigInteger.Parse(result.Stack[0].ToTvmNumberDecimal(), CultureInfo.InvariantCulture); |
| 86 | + var owner = result.Stack[1].ToTvmCell().ToBoc().RootCells[0].BeginRead().LoadAddressIntStd(); |
| 87 | + var minter = result.Stack[2].ToTvmCell().ToBoc().RootCells[0].BeginRead().LoadAddressIntStd(); |
| 88 | + |
| 89 | + return (balance, owner, minter); |
| 90 | + } |
| 91 | + |
| 92 | + /// <summary> |
| 93 | + /// Creates message that will transfer jettons from source to destination. |
| 94 | + /// </summary> |
| 95 | + /// <param name="sourceJettonAddress">Jetton wallet address to send coins from (use <see cref="GetWalletAddress">GetWalletAddress</see> if needed).</param> |
| 96 | + /// <param name="queryId">Arbitrary request number.</param> |
| 97 | + /// <param name="amount">Amount of transferred jettons <b>in elementary units</b>.</param> |
| 98 | + /// <param name="destination">Address of the new owner of the jettons (user main-wallet address, not his jetton address).</param> |
| 99 | + /// <param name="responseDestination">Address where to send a response with confirmation of a successful transfer and the rest of the incoming message Toncoins.</param> |
| 100 | + /// <param name="customPayload">Optional custom data (which is used by either sender or receiver jetton wallet for inner logic).</param> |
| 101 | + /// <param name="forwardTonAmount">The amount of nanotons to be sent to the destination address.</param> |
| 102 | + /// <param name="forwardPayload">Optional custom data that should be sent to the destination address.</param> |
| 103 | + /// <returns>Constructed and ready-to-be-sent Message (by editor/owner of <paramref name="sourceJettonAddress"/>).</returns> |
| 104 | + /// <remarks> |
| 105 | + /// <para>Your Jetton wallet address must already be deployed and active, and contain enough jettons to send.</para> |
| 106 | + /// </remarks> |
| 107 | + /// <seealso href="https://github.com/ton-blockchain/TEPs/blob/master/text/0074-jettons-standard.md#1-transfer">Transfer message in TEP</seealso> |
| 108 | + public Message CreateTransferMessage( |
| 109 | + string sourceJettonAddress, |
| 110 | + ulong queryId, |
| 111 | + BigInteger amount, |
| 112 | + string destination, |
| 113 | + string responseDestination, |
| 114 | + Cell? customPayload, |
| 115 | + decimal forwardTonAmount, |
| 116 | + Cell? forwardPayload) |
| 117 | + { |
| 118 | + var body = new CellBuilder() |
| 119 | + .StoreUInt(OPTransfer) |
| 120 | + .StoreULong(queryId) |
| 121 | + .StoreCoins(amount) |
| 122 | + .StoreAddressIntStd(destination) |
| 123 | + .StoreAddressIntStd(responseDestination) |
| 124 | + .StoreDict(customPayload) |
| 125 | + .StoreCoins(TonUtils.Coins.ToNano(forwardTonAmount)) |
| 126 | + .StoreDict(forwardPayload) |
| 127 | + ; |
| 128 | + |
| 129 | + return new Message(new AccountAddress(sourceJettonAddress)) |
| 130 | + { |
| 131 | + Amount = TonUtils.Coins.ToNano(DefaultAmount), |
| 132 | + Data = new DataRaw(new Boc(body.Build()).SerializeToBase64(), string.Empty), |
| 133 | + SendMode = DefaultSendMode, |
| 134 | + }; |
| 135 | + } |
| 136 | + |
| 137 | + /// <summary> |
| 138 | + /// Creates message that will burn specified amount of jettons. |
| 139 | + /// </summary> |
| 140 | + /// <param name="sourceJettonAddress">Jetton wallet address to send coins from (use <see cref="GetWalletAddress">GetWalletAddress</see> if needed).</param> |
| 141 | + /// <param name="queryId">Arbitrary request number.</param> |
| 142 | + /// <param name="amount">Amount of jettons to burn <b>in elementary units</b>.</param> |
| 143 | + /// <param name="responseDestination">Address where to send a response with confirmation of a successful transfer and the rest of the incoming message Toncoins.</param> |
| 144 | + /// <param name="customPayload">Optional custom data (which is used by either sender or receiver jetton wallet for inner logic).</param> |
| 145 | + /// <returns>Constructed and ready-to-be-sent Message (by editor/owner of <paramref name="sourceJettonAddress"/>).</returns> |
| 146 | + /// <remarks> |
| 147 | + /// <para>Your Jetton wallet address must already be deployed and active, and contain enough jettons to send.</para> |
| 148 | + /// </remarks> |
| 149 | + /// <seealso href="https://github.com/ton-blockchain/TEPs/blob/master/text/0074-jettons-standard.md#2-burn">Burn message in TEP</seealso> |
| 150 | + public Message CreateBurnMessage( |
| 151 | + string sourceJettonAddress, |
| 152 | + ulong queryId, |
| 153 | + BigInteger amount, |
| 154 | + string responseDestination, |
| 155 | + Cell? customPayload) |
| 156 | + { |
| 157 | + var body = new CellBuilder() |
| 158 | + .StoreUInt(OPBurn) |
| 159 | + .StoreULong(queryId) |
| 160 | + .StoreCoins(amount) |
| 161 | + .StoreAddressIntStd(responseDestination) |
| 162 | + .StoreDict(customPayload) |
| 163 | + ; |
| 164 | + |
| 165 | + return new Message(new AccountAddress(sourceJettonAddress)) |
| 166 | + { |
| 167 | + Amount = TonUtils.Coins.ToNano(DefaultAmount), |
| 168 | + Data = new DataRaw(new Boc(body.Build()).SerializeToBase64(), string.Empty), |
| 169 | + SendMode = DefaultSendMode, |
| 170 | + }; |
| 171 | + } |
| 172 | + } |
| 173 | +} |
0 commit comments