Skip to content

Commit

Permalink
feat: add jetton wallet discoverable (#6)
Browse files Browse the repository at this point in the history
ya7on authored Sep 29, 2024

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
1 parent ddbb2ff commit 8049f8f
Showing 5 changed files with 136 additions and 21 deletions.
5 changes: 3 additions & 2 deletions contracts/jetton/master.tact
Original file line number Diff line number Diff line change
@@ -2,11 +2,12 @@ import "@stdlib/deploy";
import "@stdlib/ownable";
import "./wallet.tact";
import "../teps/tep74.tact";
import "../teps/tep89.tact";
import "../messages.tact";
import "../errors.tact";

@interface("org.ton.jetton.master")
contract JettonMaster with TEP74JettonMaster, Deployable, Ownable {
contract JettonMaster with TEP74JettonMaster, TEP89JettonDiscoverable, Deployable, Ownable {
// Maximum tokens can be minted
max_supply: Int = ton("21000000");
// Current tokens minted
@@ -69,7 +70,7 @@ contract JettonMaster with TEP74JettonMaster, Deployable, Ownable {
receive(msg: JettonMint){
self.requireOwner();
nativeThrowIf(ERROR_MAX_SUPPLY_EXCEEDED, (self.current_supply + msg.amount) > self.max_supply);
let init = self.generate_wallet_state_init(msg.destination);
let init = self.discover_wallet_state_init(myAddress(), msg.destination);
let to = contractAddress(init);
send(SendParameters{
to: to,
10 changes: 10 additions & 0 deletions contracts/messages.tact
Original file line number Diff line number Diff line change
@@ -36,6 +36,16 @@ message(0x7bdd97de) JettonBurnInternal {
message(0xd53276db) Excesses {
query_id: Int as uint64;
}
message(0x2c76b973) ProvideWalletAddress {
query_id: Int as uint64;
owner_address: Address;
include_address: Bool;
}
message(0xd1735400) TakeWalletAddress {
query_id: Int as uint64;
wallet_address: Address;
owner_address: Address?;
}
message(0x133701) JettonInit {
query_id: Int as uint64;
jetton_name: String;
25 changes: 7 additions & 18 deletions contracts/teps/tep74.tact
Original file line number Diff line number Diff line change
@@ -2,6 +2,7 @@

import "@stdlib/ownable";
import "./tep64.tact";
import "./tep89.tact";
import "../messages.tact";
import "../errors.tact";
import "../consts.tact";
@@ -13,7 +14,7 @@ struct JettonMasterData {
jetton_wallet_code: Cell;
}
// TON jetton standard. Trait for jetton master
trait TEP74JettonMaster with TEP64Metadata {
trait TEP74JettonMaster with TEP64Metadata, DiscoverWalletAddress {
// Maximum tokens can be minted
max_supply: Int;
// Current tokens minted
@@ -33,7 +34,7 @@ trait TEP74JettonMaster with TEP64Metadata {

receive(msg: JettonBurnInternal){
let ctx = context();
let init = self.generate_wallet_state_init(msg.sender);
let init = self.discover_wallet_state_init(myAddress(), msg.sender);
let wallet_address = contractAddress(init);
nativeThrowUnless(ERROR_CODE_INVALID_OWNER, ctx.sender == wallet_address);
send(SendParameters{
@@ -58,14 +59,8 @@ trait TEP74JettonMaster with TEP64Metadata {
};
}

fun generate_wallet_state_init(owner: Address): StateInit {
let data = beginCell().storeRef(self.jetton_wallet_system).storeUint(0, 1).storeAddress(myAddress()
).storeAddress(owner).endCell();
return StateInit{code: self.jetton_wallet_code, data: data};
}

get fun get_wallet_address(owner: Address): Address {
let init = self.generate_wallet_state_init(owner);
let init = self.discover_wallet_state_init(myAddress(), owner);
return contractAddress(init);
}
}
@@ -75,7 +70,7 @@ struct JettonWalletData {
master: Address;
code: Cell;
}
trait TEP74JettonWallet with Ownable {
trait TEP74JettonWallet with Ownable, DiscoverWalletAddress {
const GAS_CONSUMPTION: Int = ton("0.01");
const MIN_BALANCE: Int = ton("0.01");
master: Address;
@@ -93,7 +88,7 @@ trait TEP74JettonWallet with Ownable {
((((ctx.readForwardFee() * 2) + (2 * self.GAS_CONSUMPTION)) + self.MIN_BALANCE) + msg.forward_ton_amount) <
ctx.value
);
let init = self.generate_wallet_state_init(msg.destination);
let init = self.discover_wallet_state_init(self.master, msg.destination);
let to = contractAddress(init);
send(SendParameters{
to: to,
@@ -117,7 +112,7 @@ trait TEP74JettonWallet with Ownable {
receive(msg: JettonTransferInternal){
let ctx = context();
if (ctx.sender != self.master) {
let init = self.generate_wallet_state_init(msg.from);
let init = self.discover_wallet_state_init(self.master, msg.from);
nativeThrowUnless(ERROR_CODE_INVALID_OWNER, contractAddress(init) == ctx.sender);
}
self.balance = self.balance + msg.amount;
@@ -193,10 +188,4 @@ trait TEP74JettonWallet with Ownable {
code: self.jetton_wallet_code
};
}

fun generate_wallet_state_init(owner: Address): StateInit {
let data = beginCell().storeRef(self.jetton_wallet_system).storeUint(0, 1).storeAddress(self.master
).storeAddress(owner).endCell();
return StateInit{code: self.jetton_wallet_code, data: data};
}
}
45 changes: 45 additions & 0 deletions contracts/teps/tep89.tact
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import "../messages.tact";
import "../errors.tact";
// Trait for generating jetton wallet state init
trait DiscoverWalletAddress {
// Initial code of jetton wallet
jetton_wallet_code: Cell;
// System cell of jetton wallet contract (Tact feature)
jetton_wallet_system: Cell;

fun discover_wallet_state_init(master: Address, owner: Address): StateInit {
let data = beginCell().storeRef(self.jetton_wallet_system).storeUint(0, 1).storeAddress(master
).storeAddress(owner).endCell();
return StateInit{code: self.jetton_wallet_code, data: data};
}
}
// Trait for discover jetton wallet address by owner
// https://github.com/ton-blockchain/TEPs/blob/master/text/0089-jetton-wallet-discovery.md
trait TEP89JettonDiscoverable with DiscoverWalletAddress {
// Initial code of jetton wallet
jetton_wallet_code: Cell;
// System cell of jetton wallet contract (Tact feature)
jetton_wallet_system: Cell;

receive(msg: ProvideWalletAddress){
let ctx = context();
nativeThrowUnless(ERROR_CODE_NEED_FEE, ctx.value <= ton("0.05")); // TODO
let init = self.discover_wallet_state_init(myAddress(), msg.owner_address);
let address = contractAddress(init);
let owner_address: Address? = null;
if (msg.include_address) {
owner_address = msg.owner_address;
}
send(SendParameters{
to: ctx.sender,
value: 0,
mode: SendRemainingValue,
body: TakeWalletAddress{
query_id: msg.query_id,
wallet_address: address,
owner_address: owner_address
}.toCell()
}
);
}
}
72 changes: 71 additions & 1 deletion tests/JettonMaster.spec.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Blockchain, SandboxContract, TreasuryContract } from '@ton/sandbox';
import { Builder, toNano } from '@ton/core';
import { beginCell, Builder, toNano } from '@ton/core';
import { JettonWallet } from '../build/Jetton/tact_JettonWallet';
import { JettonMaster } from '../build/Jetton/tact_JettonMaster';
import '@ton/test-utils';
@@ -275,4 +275,74 @@ describe('JettonMaster', () => {
exitCode: 132,
});
});

it('should discover address', async () => {
let discoverResult = await jettonMaster.send(
deployer.getSender(),
{
value: toNano("0.05"),
},
{
$$type: 'ProvideWalletAddress',
query_id: 0n,
owner_address: other.address,
include_address: false,
}
);
expect(discoverResult.transactions).toHaveTransaction({
from: deployer.address,
to: jettonMaster.address,
success: true,
deploy: false,
op: 0x2c76b973,
});
expect(discoverResult.transactions).toHaveTransaction({
from: jettonMaster.address,
to: deployer.address,
success: true,
deploy: false,
op: 0xd1735400,
body: beginCell()
.storeUint(0xd1735400, 32)
.storeUint(0, 64)
.storeAddress(otherJettonWallet.address)
.storeAddress(null)
.endCell()
});
});

it('should discover include address', async () => {
let discoverResult = await jettonMaster.send(
deployer.getSender(),
{
value: toNano("0.05"),
},
{
$$type: 'ProvideWalletAddress',
query_id: 0n,
owner_address: other.address,
include_address: true,
}
);
expect(discoverResult.transactions).toHaveTransaction({
from: deployer.address,
to: jettonMaster.address,
success: true,
deploy: false,
op: 0x2c76b973,
});
expect(discoverResult.transactions).toHaveTransaction({
from: jettonMaster.address,
to: deployer.address,
success: true,
deploy: false,
op: 0xd1735400,
body: beginCell()
.storeUint(0xd1735400, 32)
.storeUint(0, 64)
.storeAddress(otherJettonWallet.address)
.storeAddress(other.address)
.endCell()
});
});
});

0 comments on commit 8049f8f

Please sign in to comment.