-
Notifications
You must be signed in to change notification settings - Fork 1
feat: Return Jettons, that were sent by accident #59
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
f67d95e
dea91ef
7ab2116
edb02e8
a17e0ca
bd4a129
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,4 @@ | ||
yarn fmt | ||
yarn tact-fmt | ||
yarn tact-fmt --write ./ | ||
yarn deduplicate | ||
yarn lint-staged |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,6 +3,24 @@ import "../math"; | |
import "../core/messages.tact"; | ||
import "../core/liquidity-deposit"; | ||
import "../core/amm-pool"; | ||
import "../utils/utils"; | ||
|
||
message(0xf8a7ea5) ReturnJettonsViaJettonTransfer { | ||
queryId: Int as uint64; | ||
amount: Int as coins; | ||
destination: Address; | ||
responseDestination: Address?; | ||
customPayload: Cell?; | ||
forwardTonAmount: Int as coins; | ||
forwardPayload: Slice as remaining; | ||
} | ||
|
||
message(0x7362d09c) UnexpectedJettonNotification { | ||
Shvandre marked this conversation as resolved.
Show resolved
Hide resolved
|
||
queryId: Int as uint64; | ||
amount: Int as coins; | ||
sender: Address; | ||
forwardPayload: Slice as remaining; | ||
} | ||
|
||
contract TonVault( | ||
admin: Address, | ||
|
@@ -44,6 +62,27 @@ contract TonVault( | |
}); | ||
} | ||
|
||
// Someone possibly transferred us jettons by accident | ||
receive(msg: UnexpectedJettonNotification) { | ||
message(MessageParameters { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe let's separate this refund to standalone function? It is used three times with the same exact code |
||
mode: SendRemainingValue, | ||
value: 0, | ||
body: ReturnJettonsViaJettonTransfer { | ||
queryId: msg.queryId, | ||
amount: msg.amount, | ||
destination: msg.sender, | ||
responseDestination: msg.sender, | ||
customPayload: null, | ||
forwardTonAmount: 1, | ||
forwardPayload: sliceWithOneZeroBit(), | ||
}.toCell(), | ||
to: sender(), | ||
bounce: true, | ||
}); | ||
commit(); | ||
require(false, "TonVault: Jetton transfer must be performed to correct Jetton Vault"); | ||
} | ||
|
||
receive(msg: AddLiquidityPartTon) { | ||
// TODO: exact tests for this | ||
nativeReserve(msg.amountIn, ReserveExact | ReserveAddOriginalBalance); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -134,4 +134,125 @@ describe("Proofs", () => { | |
) | ||
expect(await jettonVaultInstance.getInited()).toBe(false) | ||
}) | ||
test("Jettons are returned if proof type is incorrect", async () => { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Let's add classic |
||
const blockchain = await Blockchain.create() | ||
const vaultSetup = await createJettonVault(blockchain) | ||
|
||
const _ = await vaultSetup.deploy() | ||
const mockActionPayload = beginCell() | ||
.storeStringTail("Random action that does not mean anything") | ||
.endCell() | ||
|
||
const sendNotifyWithNoProof = await vaultSetup.treasury.transfer( | ||
vaultSetup.vault.address, | ||
toNano(0.5), | ||
createJettonVaultMessage( | ||
// We can use any Jetton Vault opcode here because we don't need an actual operation here | ||
LPDepositPartOpcode, | ||
mockActionPayload, | ||
{ | ||
proofType: 0n, // No proof attached | ||
}, | ||
), | ||
) | ||
|
||
const toVaultTx = flattenTransaction( | ||
findTransactionRequired(sendNotifyWithNoProof.transactions, { | ||
to: vaultSetup.vault.address, | ||
op: JettonVault.opcodes.JettonNotifyWithActionRequest, | ||
success: true, // Because commit was called | ||
exitCode: JettonVault.errors["JettonVault: Proof is invalid"], | ||
}), | ||
) | ||
|
||
expect(sendNotifyWithNoProof.transactions).toHaveTransaction({ | ||
from: vaultSetup.vault.address, | ||
to: toVaultTx.from, | ||
op: JettonVault.opcodes.JettonTransfer, | ||
success: true, | ||
}) | ||
|
||
expect(await vaultSetup.isInited()).toBe(false) | ||
}) | ||
|
||
test("Jettons are returned if sent to wrong vault", async () => { | ||
const blockchain = await Blockchain.create() | ||
// Create and set up a correct jetton vault | ||
const vaultSetup = await createJettonVault(blockchain) | ||
const _ = await vaultSetup.deploy() | ||
|
||
// Create a different jetton (wrong one) for testing | ||
const wrongJetton = await createJetton(blockchain) | ||
|
||
// Get the initial balance of the wrong jetton wallet | ||
const initialWrongJettonBalance = await wrongJetton.wallet.getJettonBalance() | ||
|
||
// Create a mock payload to use with the transfer | ||
const mockPayload = beginCell() | ||
.store( | ||
storeLPDepositPart({ | ||
$$type: "LPDepositPart", | ||
liquidityDepositContract: randomAddress(0), // Mock LP contract address | ||
additionalParams: { | ||
$$type: "AdditionalParams", | ||
minAmountToDeposit: 0n, | ||
lpTimeout: 0n, | ||
payloadOnSuccess: null, | ||
payloadOnFailure: null, | ||
}, | ||
}), | ||
) | ||
.endCell() | ||
|
||
// Number of jettons to send to the wrong vault | ||
const amountToSend = toNano(0.5) | ||
|
||
// First, we need to initialize the vault with the correct jettons | ||
const _initVault = await vaultSetup.treasury.transfer( | ||
vaultSetup.vault.address, | ||
amountToSend, | ||
createJettonVaultMessage( | ||
// We can use any Jetton Vault opcode here because we don't need an actual operation here | ||
LPDepositPartOpcode, | ||
mockPayload, | ||
{ | ||
proofType: PROOF_TEP89, | ||
}, | ||
), | ||
) | ||
expect(await vaultSetup.isInited()).toBeTruthy() | ||
|
||
// Send wrong Jetton to the vault | ||
const sendJettonsToWrongVault = await wrongJetton.transfer( | ||
vaultSetup.vault.address, | ||
amountToSend, | ||
createJettonVaultMessage(LPDepositPartOpcode, mockPayload, { | ||
proofType: PROOF_TEP89, | ||
}), | ||
) | ||
|
||
// Verify that the transaction to the vault has occurred but failed due to the wrong jetton | ||
const toVaultTx = flattenTransaction( | ||
findTransactionRequired(sendJettonsToWrongVault.transactions, { | ||
to: vaultSetup.vault.address, | ||
op: JettonVault.opcodes.JettonNotifyWithActionRequest, | ||
success: true, // Because commit was called | ||
exitCode: JettonVault.errors["JettonVault: Sender must be jetton wallet"], | ||
}), | ||
) | ||
|
||
// Check that the jettons were sent back to the original wallet | ||
expect(sendJettonsToWrongVault.transactions).toHaveTransaction({ | ||
from: vaultSetup.vault.address, | ||
to: toVaultTx.from, | ||
op: JettonVault.opcodes.JettonTransfer, | ||
success: true, | ||
}) | ||
|
||
expect(await vaultSetup.isInited()).toBeTruthy() | ||
|
||
// Verify that the balance of the wrong jetton wallet is unchanged (jettons returned) | ||
const finalWrongJettonBalance = await wrongJetton.wallet.getJettonBalance() | ||
expect(finalWrongJettonBalance).toEqual(initialWrongJettonBalance) | ||
}) | ||
}) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
import {Blockchain} from "@ton/sandbox" | ||
import {createJetton, createTonVault} from "../utils/environment" | ||
import {beginCell} from "@ton/core" | ||
import {findTransactionRequired, flattenTransaction} from "@ton/test-utils" | ||
import {randomInt} from "node:crypto" | ||
import {TonVault} from "../output/DEX_TonVault" | ||
|
||
describe("TON Vault", () => { | ||
test("Jettons are returned if sent to TON Vault", async () => { | ||
const blockchain = await Blockchain.create() | ||
const vaultSetup = await createTonVault(blockchain) | ||
|
||
const _ = await vaultSetup.deploy() | ||
const mockActionPayload = beginCell().storeStringTail("Random payload").endCell() | ||
|
||
const jetton = await createJetton(blockchain) | ||
const initialBalance = await jetton.wallet.getJettonBalance() | ||
const numberOfJettons = BigInt(randomInt(0, 100000000000)) | ||
const sendResult = await jetton.transfer( | ||
vaultSetup.vault.address, | ||
numberOfJettons, | ||
mockActionPayload, | ||
) | ||
|
||
const toVaultTx = flattenTransaction( | ||
findTransactionRequired(sendResult.transactions, { | ||
to: vaultSetup.vault.address, | ||
op: TonVault.opcodes.UnexpectedJettonNotification, | ||
success: true, // Because commit was called | ||
exitCode: | ||
TonVault.errors[ | ||
"TonVault: Jetton transfer must be performed to correct Jetton Vault" | ||
], | ||
}), | ||
) | ||
|
||
expect(sendResult.transactions).toHaveTransaction({ | ||
from: vaultSetup.vault.address, | ||
to: toVaultTx.from, | ||
op: TonVault.opcodes.ReturnJettonsViaJettonTransfer, | ||
success: true, | ||
}) | ||
const finalJettonBalance = await jetton.wallet.getJettonBalance() | ||
expect(finalJettonBalance).toEqual(initialBalance) | ||
}) | ||
}) |
Uh oh!
There was an error while loading. Please reload this page.