You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

233 lines
7.1 KiB

import "@stdlib/ownable";
import "./messages";
import "./linker";
import "./constants";
@interface("org.ton.jetton.wallet")
contract TONBWallet {
const minTonsForStorage: Int = ton("0.01");
const gasConsumption: Int = ton("0.01");
balance: Int;
owner: Address;
master: Address;
blacklisted: Bool = false;
linker: Int?;
linker_address: Address?;
init(master: Address, owner: Address) {
self.balance = 0;
self.owner = owner;
self.master = master;
}
receive(msg: TokenTransfer) {
require(!self.blacklisted, "Wallet is blacklisted");
// Check sender
let ctx: Context = context();
require(ctx.sender == self.owner, "Invalid sender");
// Update balance
self.balance = self.balance - msg.amount;
require(self.balance >= 0, "Invalid balance");
// Gas checks
let fwdFee: Int = ctx.readForwardFee();
let fwdCount: Int = 1;
if (msg.forwardTonAmount > 0) {
fwdCount = 2;
}
require(ctx.value > transfer_gas_consumption, "Invalid value");
// Send tokens
let init: StateInit = initOf TONBWallet(self.master, msg.destination);
let walletAddress: Address = contractAddress(init);
send(SendParameters{
to: walletAddress,
value: 0,
mode: SendRemainingValue,
bounce: true,
body: TokenTransferInternal{
amount: msg.amount,
queryId: msg.queryId,
from: self.owner,
responseAddress: self.owner,
forwardTonAmount: msg.forwardTonAmount,
forwardPayload: msg.forwardPayload,
setLinker: null,
setLinkerAddress: null
}.toCell(),
code: init.code,
data: init.data
});
}
receive(msg: TokenTransferInternal) {
if(self.blacklisted){
send(SendParameters{
to: self.master,
value: 0,
mode: SendRemainingValue,
body: TokenBurnNotification{
queryId: msg.queryId,
amount: msg.amount,
owner: self.owner,
responseAddress: self.owner
}.toCell()
});
return;
}
// Check sender
let ctx: Context = context();
if (self.linker == null) {
if (msg.setLinker != null) {
self.linker = msg.setLinker;
self.linker_address = msg.setLinkerAddress;
}
}
if (ctx.sender != self.master/* && ctx.sender != self.linker_address*/) {
let is_from_linker: Bool = false;
if (self.linker_address != null) {
if (ctx.sender == self.linker_address!!) {
is_from_linker = true;
}
}
if (!is_from_linker) {
let sinit: StateInit = initOf TONBWallet(self.master, msg.from);
require(contractAddress(sinit) == ctx.sender, "Invalid sender");
}
}
// Update balance
self.balance = self.balance + msg.amount;
require(self.balance >= 0, "Invalid balance");
// Adjust value for gas
let msgValue: Int = ctx.value;
let tonBalanceBeforeMsg: Int = myBalance() - msgValue;
let storageFee: Int = self.minTonsForStorage - min(tonBalanceBeforeMsg, self.minTonsForStorage);
if (self.linker == null) {
// request a linker
send(SendParameters{
to: self.master,
value: 0,
mode: SendRemainingValue,
body: RequestLinker {
client: self.owner
}.toCell()
});
return;
}
msgValue = msgValue - (storageFee + self.gasConsumption);
// Forward ton
if (msg.forwardTonAmount > 0) {
let fwdFee: Int = ctx.readForwardFee();
msgValue = msgValue - (msg.forwardTonAmount + fwdFee);
send(SendParameters{
to: self.owner,
value: msg.forwardTonAmount,
bounce: false,
body: TokenNotification{
queryId: msg.queryId,
amount: msg.amount,
from: msg.from,
forwardPayload: msg.forwardPayload
}.toCell()
});
}
// Cashback
if (msg.responseAddress != null && msgValue > 0) {
send(SendParameters{
to: msg.responseAddress!!,
value: msgValue,
bounce: false,
body: TokenExcesses{
queryId: msg.queryId
}.toCell()
});
}
}
receive(msg: TokenBurn) {
// Check sender
let ctx: Context = context();
require(ctx.sender == self.owner || ctx.sender == self.master || ctx.sender == self.linker_address, "Invalid sender");
// Update balance
self.balance = self.balance - msg.amount;
require(self.balance >= 0, "Invalid balance");
// // Gas checks
let fwdFee: Int = ctx.readForwardFee();
require(ctx.value > 2 * self.gasConsumption + self.minTonsForStorage, "Invalid value");
// Burn tokens
send(SendParameters{
to: self.master,
value: 0,
mode: SendRemainingValue,
bounce: true,
body: TokenBurnNotification{
queryId: msg.queryId,
amount: msg.amount,
owner: self.owner,
responseAddress: self.owner
}.toCell()
});
}
receive(_msg: BlacklistWallet) {
// Check sender
let ctx: Context = context();
require(ctx.sender == self.master || ctx.sender == self.linker_address, "Invalid sender");
// Blacklist wallet
self.blacklisted = true;
let amount: Int = self.balance;
self.balance = 0;
send(SendParameters{
to: self.master,
value: 0,
mode: SendRemainingValue,
bounce: true,
body: TokenBurnNotification{
queryId: 0,
amount: amount,
owner: self.owner,
responseAddress: self.owner
}.toCell()
});
}
bounced(msg: Slice) {
// Parse bounced message
msg.skipBits(32); // 0xFFFFFFFF
let op: Int = msg.loadUint(32);
let queryId: Int = msg.loadUint(64);
let jettonAmount: Int = msg.loadCoins();
require(op == 0x178d4519 || op == 0x7bdd97de, "Invalid bounced message");
// Update balance
self.balance = self.balance + jettonAmount;
}
get fun get_wallet_data(): JettonWalletData {
return JettonWalletData{
balance: self.balance,
owner: self.owner,
master: self.master,
walletCode: (initOf TONBWallet(self.master, self.owner)).code
};
}
}