|
|
|
|
|
|
|
import "@stdlib/ownable";
|
|
|
|
import "./wallet";
|
|
|
|
import "./linker";
|
|
|
|
import "./constants";
|
|
|
|
import "./staking";
|
|
|
|
|
|
|
|
|
|
|
|
@interface("org.ton.jetton.master")
|
|
|
|
trait TONBTrait with Ownable, StakingTrait {
|
|
|
|
|
|
|
|
//
|
|
|
|
// Storage
|
|
|
|
//
|
|
|
|
|
|
|
|
totalSupply: Int;
|
|
|
|
mintable: Bool;
|
|
|
|
owner: Address;
|
|
|
|
content: Cell?;
|
|
|
|
first_linker: Address?;
|
|
|
|
last_linker: Address?;
|
|
|
|
n_linkers: Int = 0;
|
|
|
|
staking_pool: Address?;
|
|
|
|
in_the_pool: Int = 0;
|
|
|
|
withdrawal_requests: WithdrawalRequests;
|
|
|
|
|
|
|
|
//
|
|
|
|
// Receivers
|
|
|
|
//
|
|
|
|
|
|
|
|
receive(msg: TokenUpdateContent) {
|
|
|
|
|
|
|
|
// Allow changing content only by owner
|
|
|
|
self.requireOwner();
|
|
|
|
|
|
|
|
// Update content
|
|
|
|
self.content = msg.content;
|
|
|
|
}
|
|
|
|
|
|
|
|
receive(msg: TokenBurnNotification) {
|
|
|
|
|
|
|
|
// Check wallet
|
|
|
|
self.requireWallet(msg.owner);
|
|
|
|
|
|
|
|
// Update supply
|
|
|
|
self.totalSupply = self.totalSupply - msg.amount;
|
|
|
|
let available: Int = myBalance() - tonb_floor - withdraw_gas_consumption;
|
|
|
|
if (available < msg.amount) {
|
|
|
|
/* if not enough tokens, withdraw what we have, send some tokens to the owner, add a withdrawal request (-withdrawal_gas) */
|
|
|
|
let diff: Int = msg.amount - available;
|
|
|
|
send(SendParameters{
|
|
|
|
to: msg.owner,
|
|
|
|
value: myBalance() - tonb_floor - withdraw_gas_consumption,
|
|
|
|
bounce: false
|
|
|
|
});
|
|
|
|
let body: Cell = TokenTransferInternal{
|
|
|
|
amount: diff,
|
|
|
|
queryId: 0,
|
|
|
|
from: myAddress(),
|
|
|
|
responseAddress: myAddress(),
|
|
|
|
forwardTonAmount: 0,
|
|
|
|
forwardPayload: emptySlice(),
|
|
|
|
setLinker: null,
|
|
|
|
setLinkerAddress: null
|
|
|
|
}.toCell();
|
|
|
|
let walletAddress: Address = self.get_wallet_address(msg.owner);
|
|
|
|
send(SendParameters{
|
|
|
|
to: walletAddress,
|
|
|
|
value: 0,
|
|
|
|
bounce: false,
|
|
|
|
mode: SendRemainingValue,
|
|
|
|
body: body
|
|
|
|
});
|
|
|
|
let ctx: Context = context();
|
|
|
|
self.requestWithdrawal(ctx.sender, diff);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Withdraw
|
|
|
|
send(SendParameters{
|
|
|
|
to: msg.owner,
|
|
|
|
value: msg.amount - withdraw_gas_consumption,
|
|
|
|
bounce: false
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
receive(msg: BlacklistWallet) {
|
|
|
|
// Allow blacklisting only by owner
|
|
|
|
self.requireOwner();
|
|
|
|
|
|
|
|
// Blacklist wallet
|
|
|
|
let winit: StateInit = self.getJettonWalletInit(msg.wallet);
|
|
|
|
let walletAddress: Address = contractAddress(winit);
|
|
|
|
send(SendParameters{
|
|
|
|
to: walletAddress,
|
|
|
|
value: 0,
|
|
|
|
mode: SendRemainingValue,
|
|
|
|
bounce: false,
|
|
|
|
body: msg.toCell()
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// Get Methods
|
|
|
|
//
|
|
|
|
|
|
|
|
get fun get_wallet_address(owner: Address): Address {
|
|
|
|
let winit: StateInit = self.getJettonWalletInit(owner);
|
|
|
|
return contractAddress(winit);
|
|
|
|
}
|
|
|
|
|
|
|
|
get fun get_jetton_data(): JettonData {
|
|
|
|
let code: Cell = self.getJettonWalletInit(myAddress()).code;
|
|
|
|
return JettonData{
|
|
|
|
totalSupply: self.totalSupply,
|
|
|
|
mintable: self.mintable,
|
|
|
|
owner: self.owner,
|
|
|
|
content: self.content,
|
|
|
|
walletCode: code
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// Private Methods
|
|
|
|
//
|
|
|
|
|
|
|
|
fun mint(to: Address, amount: Int, responseAddress: Address?) {
|
|
|
|
|
|
|
|
// Update total supply
|
|
|
|
self.totalSupply = self.totalSupply + amount;
|
|
|
|
|
|
|
|
// Create message
|
|
|
|
let winit: StateInit = self.getJettonWalletInit(to);
|
|
|
|
let walletAddress: Address = contractAddress(winit);
|
|
|
|
let linker_init: StateInit = initOf Linker(self.n_linkers, walletAddress, myAddress());
|
|
|
|
let linker_address: Address = contractAddress(linker_init);
|
|
|
|
|
|
|
|
send(SendParameters{
|
|
|
|
to: linker_address,
|
|
|
|
value: linker_credit,
|
|
|
|
bounce: false,
|
|
|
|
body: SetLinkerNeighbor{
|
|
|
|
neighbor: self.last_linker
|
|
|
|
}.toCell(),
|
|
|
|
code: linker_init.code,
|
|
|
|
data: linker_init.data
|
|
|
|
});
|
|
|
|
self.last_linker = linker_address;
|
|
|
|
self.n_linkers = self.n_linkers + 1;
|
|
|
|
let wallet_msg_body: Cell = TokenTransferInternal{
|
|
|
|
amount: amount,
|
|
|
|
queryId: 0,
|
|
|
|
from: myAddress(),
|
|
|
|
responseAddress: responseAddress,
|
|
|
|
forwardTonAmount: 0,
|
|
|
|
forwardPayload: emptySlice(),
|
|
|
|
setLinker: self.n_linkers - 1,
|
|
|
|
setLinkerAddress: linker_address
|
|
|
|
}.toCell();
|
|
|
|
send(SendParameters{
|
|
|
|
to: walletAddress,
|
|
|
|
value: wallet_credit,
|
|
|
|
bounce: false,
|
|
|
|
body: wallet_msg_body,
|
|
|
|
code: winit.code,
|
|
|
|
data: winit.data
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
receive(msg: RequestLinker) {
|
|
|
|
let ctx: Context = context();
|
|
|
|
let winit: StateInit = self.getJettonWalletInit(msg.client);
|
|
|
|
let walletAddress: Address = contractAddress(winit);
|
|
|
|
require(walletAddress == ctx.sender, "invalid sender");
|
|
|
|
let linker_init: StateInit = initOf Linker(self.n_linkers, walletAddress, myAddress());
|
|
|
|
let linker_address: Address = contractAddress(linker_init);
|
|
|
|
send(SendParameters{
|
|
|
|
to: linker_address,
|
|
|
|
value: linker_credit,
|
|
|
|
bounce: false,
|
|
|
|
body: SetLinkerNeighbor{
|
|
|
|
neighbor: self.last_linker
|
|
|
|
}.toCell(),
|
|
|
|
code: linker_init.code,
|
|
|
|
data: linker_init.data
|
|
|
|
});
|
|
|
|
self.last_linker = linker_address;
|
|
|
|
self.n_linkers = self.n_linkers + 1;
|
|
|
|
let wallet_msg_body: Cell = TokenTransferInternal{
|
|
|
|
amount: 0,
|
|
|
|
queryId: 0,
|
|
|
|
from: myAddress(),
|
|
|
|
responseAddress: myAddress(),
|
|
|
|
forwardTonAmount: 0,
|
|
|
|
forwardPayload: emptySlice(),
|
|
|
|
setLinker: self.n_linkers - 1,
|
|
|
|
setLinkerAddress: linker_address
|
|
|
|
}.toCell();
|
|
|
|
send(SendParameters{
|
|
|
|
to: walletAddress,
|
|
|
|
value: wallet_credit,
|
|
|
|
bounce: false,
|
|
|
|
body: wallet_msg_body,
|
|
|
|
code: winit.code,
|
|
|
|
data: winit.data
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
fun burn(from: Address, amount: Int, responseAddress: Address?) {
|
|
|
|
|
|
|
|
// Create message
|
|
|
|
let winit: StateInit = self.getJettonWalletInit(from);
|
|
|
|
let walletAddress: Address = contractAddress(winit);
|
|
|
|
send(SendParameters{
|
|
|
|
to: walletAddress,
|
|
|
|
value: 0,
|
|
|
|
bounce: false,
|
|
|
|
mode: SendRemainingValue,
|
|
|
|
body: TokenBurn{
|
|
|
|
amount: amount,
|
|
|
|
queryId: 0,
|
|
|
|
responseAddress: responseAddress,
|
|
|
|
owner: from
|
|
|
|
}.toCell(),
|
|
|
|
code: winit.code,
|
|
|
|
data: winit.data
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
fun requireWallet(owner: Address) {
|
|
|
|
let ctx: Context = context();
|
|
|
|
let winit: StateInit = self.getJettonWalletInit(owner);
|
|
|
|
require(contractAddress(winit) == ctx.sender, "Invalid sender");
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual fun getJettonWalletInit(address: Address): StateInit {
|
|
|
|
return initOf TONBWallet(myAddress(), address);
|
|
|
|
}
|
|
|
|
}
|