Skip to content
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

Jetton Migrations interface #183

Open
wants to merge 31 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
2400051
add migration button for minters with old code
Gusarich Apr 4, 2023
fa0ec5e
migration popup window
Gusarich Apr 6, 2023
e2dac6d
update migrationpopup
Gusarich Apr 6, 2023
2adfb7e
add loading window after clicking "Migrate"
Gusarich May 21, 2023
80150fe
add redirect via button after success
Gusarich May 21, 2023
3ad9442
split `sendTransaction` into three separate functions
Gusarich May 21, 2023
9066dca
add `isNewMinterDeployed`, `isMigrationMasterDeployed` and `mintedJet…
Gusarich May 21, 2023
0aad937
refactor the code
Gusarich May 21, 2023
9c3189e
add possibility to resume
Gusarich May 21, 2023
425b478
add connection check
Gusarich May 21, 2023
b58bdf1
real check for `isNewMinterDeployed`
Gusarich May 21, 2023
437c4e0
show migration button only if connected wallet and if migration didn'…
Gusarich May 24, 2023
12e1ea9
add a button that will redirect user to a new version of the minter
Gusarich May 24, 2023
d9526dc
fix redirects
Gusarich Jun 6, 2023
2c107dc
add migration master and helper contracts
Gusarich Jun 29, 2023
b9f8986
add `migrateBody`
Gusarich Jun 29, 2023
9d0eb05
real check for 2nd transaction
Gusarich Jun 29, 2023
e14375d
actual check for 3rd transaction
Gusarich Jun 29, 2023
02b7f18
send an actual first transaction
Gusarich Jun 29, 2023
c59ae0d
fix
Gusarich Jun 29, 2023
7e741e6
send an actual second transaction
Gusarich Jun 29, 2023
28895fa
send an actual third transaction + fixes
Gusarich Jun 29, 2023
6db5019
remove the "New version" button and create a new pop-up window for us…
Gusarich Jul 1, 2023
0be6d49
wip: actually working user migraiton interface
Gusarich Jul 2, 2023
9a9bc0d
actually deploy migration helper
Gusarich Jul 2, 2023
1d456f7
fix display of the amount
Gusarich Jul 2, 2023
84979b8
migration is working correctly. todo: fix little bugs
Gusarich Jul 2, 2023
12a218e
Merge branch 'main' into main
Gusarich Jul 2, 2023
cc2db81
fixes
Gusarich Jul 2, 2023
e8cf5db
proper work with rejects
Gusarich Jul 6, 2023
2d7abdc
remove console logs
Gusarich Jul 6, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ const App = () => {
<Route path="/" element={<ContentWrapper />}>
<Route path={ROUTES.deployer} element={<DeployerPage />} />
<Route path={ROUTES.jettonId} element={<Jetton />} />
<Route path={ROUTES.migration} element={<Jetton />} />
</Route>
</Route>
</Routes>
Expand Down
1 change: 1 addition & 0 deletions src/consts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ const ROUTES = {
deployer: "/",
jetton: "/jetton",
jettonId: "/jetton/:id",
migration: "/jetton/:id/migration/:migrationId",
};

const APP_GRID = 1156;
Expand Down
3 changes: 3 additions & 0 deletions src/lib/contracts/MigrationHelper.compiled.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"hex": "b5ee9c7241020701000122000114ff00f4a413f4bcf2c80b010202d00302008969c08208403e29fa97232c7c4f2cfd400fe80be10b3c5be10f3c5b2c0208402faf0803e80b2c03e10f3c5b25c60063232c17e1073c5a08403b9aca03e80b2dab3325c7ec0203f7d76d176fd906ba4e000492f827001410808f0d1805cf968fcf6a2687d207d2000fc317d2000fc31ea187c142cb82a1009aa0a01e428027d012c678b00e78b666491646580897a007a00658064fc80383a6465816503e5ffe4e87c30e86981fd201800b8d8492f81f000e98f90c1083cf23a475d7187e99ffd001800c060504003af006708018c8cb05f843cf1602821011e1a300a112fa02cb6ac970fb0000320182107362d09cba945f03db31e1f84112c705b3935bdb31e0001831f84312c705b3935bdb31e0512f215e"
}
3 changes: 3 additions & 0 deletions src/lib/contracts/MigrationMaster.compiled.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"hex": "b5ee9c7241020a010001df000114ff00f4a413f4bcf2c80b01020162030200eba0df8dda89a1f481f481a9a861f050a084e0a84026a8280790a009f404b19e2c039e2d99924591960225e801e801960193f200e0e9919605940f97ff93a1f0c3f05004e0a84026a8280790a009f404b19e2c039e2d99924591960225e801e801960193f200e0e9919605940f97ff93a1f0c5f083f0850202cc0504005fdb841082caf83de64658f89659fac7d017c14678b658064b8c00c646582fc20e78b41057d78407d0165b56664b8fd8040201200706007bf3810410807c53f52e4658f8a659fa8027d0110e78b00e78b658041057d78407d01658064b8c00c646582fc21678b410802faf0807d0165b56664b8fd8040129d106ba4e000492f827001410805f5e1005cf968fcc0801fced44d0fa40fa40d4d430f828504270542013541403c85004fa0258cf1601cf16ccc922c8cb0112f400f400cb00c9f9007074c8cb02ca07cbffc9d0f861f8280270542013541403c85004fa0258cf1601cf16ccc922c8cb0112f400f400cb00c9f9007074c8cb02ca07cbffc9d0f862d0d303fa403002d31f0271b0f8411409007cc705b313b10282107362d09cbd12b1915be0d33ffa00fa403171d721547120f00702f008708018c8cb0558cf160282100bebc200a112fa02cb6ac970fb0041907573"
}
17 changes: 15 additions & 2 deletions src/lib/deploy-controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,8 @@ class JettonDeployController {
toAddress: string,
fromAddress: string,
ownerJettonWallet: string,
customValue?: number,
customForwardValue?: number,
) {
const tc = await getClient();

Expand All @@ -164,9 +166,14 @@ class JettonDeployController {
messages: [
{
address: ownerJettonWallet,
amount: toNano(0.05).toString(),
amount: toNano(customValue || 0.05).toString(),
stateInit: undefined,
payload: transfer(Address.parse(toAddress), Address.parse(fromAddress), amount)
payload: transfer(
Address.parse(toAddress),
Address.parse(fromAddress),
amount,
customForwardValue,
)
.toBoc()
.toString("base64"),
},
Expand Down Expand Up @@ -256,6 +263,12 @@ class JettonDeployController {
};
}

async getJettonMinterCode(contractAddress: Address) {
const client = getClient();
const code = (await (await client).getContractState(contractAddress)).code!;
return code;
}

async fixFaultyJetton(
contractAddress: Address,
data: {
Expand Down
9 changes: 7 additions & 2 deletions src/lib/jetton-minter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -235,15 +235,20 @@ export function burn(amount: BN, responseAddress: Address) {
.endCell();
}

export function transfer(to: Address, from: Address, jettonAmount: BN) {
export function transfer(
to: Address,
from: Address,
jettonAmount: BN,
customForwardValue?: number,
) {
return beginCell()
.storeUint(OPS.Transfer, 32)
.storeUint(1, 64)
.storeCoins(jettonAmount)
.storeAddress(to)
.storeAddress(from)
.storeBit(false)
.storeCoins(toNano(0.001))
.storeCoins(toNano(customForwardValue || 0.001))
.storeBit(false) // forward_payload in this slice, not separate cell
.endCell();
}
Expand Down
141 changes: 141 additions & 0 deletions src/lib/migrations.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
import { Cell, beginCell, Address, TonClient } from "ton";

import masterHex from "./contracts/MigrationMaster.compiled.json";
import helperHex from "./contracts/MigrationHelper.compiled.json";
import { useTonAddress, useTonConnectUI, TonConnectUI } from "@tonconnect/ui-react";
import { getClient } from "./get-ton-client";
import BN from "bn.js";
import { ContractDeployer } from "./contract-deployer";
import WalletConnection from "services/wallet-connection";
import { waitForContractDeploy } from "./utils";

export const MIGRATION_MASTER_CODE = Cell.fromBoc(masterHex.hex)[0];
export const MIGRATION_HELPER_CODE = Cell.fromBoc(helperHex.hex)[0]; // code cell from build output

enum OPS {
Migrate = 0x79e4748e,
}

export const MIGRATION_MASTER_DEPLOY_GAS = new BN("100000000");
export const MIGRATION_HELPER_DEPLOY_GAS = new BN("20000000");

export type MigrationMasterConfig = {
oldJettonMinter: Address;
newJettonMinter: Address;
oldWalletCode?: Cell;
newWalletCode?: Cell;
};

export async function migrationMasterConfigToCell(config: MigrationMasterConfig): Promise<Cell> {
const client = await getClient();
const oldWalletCode = Cell.fromBoc(
Buffer.from(
(await client.callGetMethod(config.oldJettonMinter, "get_jetton_data")).stack[4][1].bytes,
"base64",
).toString("hex"),
)[0];
const newWalletCode = Cell.fromBoc(
Buffer.from(
(await client.callGetMethod(config.newJettonMinter, "get_jetton_data")).stack[4][1].bytes,
"base64",
).toString("hex"),
)[0];

return beginCell()
.storeAddress(config.oldJettonMinter)
.storeAddress(config.newJettonMinter)
.storeRef(config.oldWalletCode || oldWalletCode)
.storeRef(config.newWalletCode || newWalletCode)
.endCell();
}

export type MigrationHelperConfig = {
oldJettonMinter: Address;
migrationMaster: Address;
recipient: Address;
oldWalletCode?: Cell;
};

export async function migrationHelperConfigToCell(config: MigrationHelperConfig): Promise<Cell> {
const client = await getClient();
const oldWalletCode = Cell.fromBoc(
Buffer.from(
(await client.callGetMethod(config.oldJettonMinter, "get_jetton_data")).stack[4][1].bytes,
"base64",
).toString("hex"),
)[0];
return beginCell()
.storeAddress(config.oldJettonMinter)
.storeAddress(config.migrationMaster)
.storeAddress(config.recipient)
.storeRef(config.oldWalletCode || oldWalletCode)
.endCell();
}

export function migrateBody(amount: BN): Cell {
return beginCell().storeUint(OPS.Migrate, 32).storeUint(1, 64).storeCoins(amount).endCell();
}

export async function createMigrationMaster(
config: MigrationMasterConfig,
tonConnection: TonConnectUI,
owner: Address,
): Promise<Address> {
const contractDeployer = new ContractDeployer();
const tc = await getClient();

// params.onProgress?.(JettonDeployState.BALANCE_CHECK);
const balance = await tc.getBalance(owner);
if (balance.lt(MIGRATION_MASTER_DEPLOY_GAS))
throw new Error("Not enough balance in deployer wallet");
const params = {
code: MIGRATION_MASTER_CODE,
data: await migrationMasterConfigToCell(config),
deployer: owner, //anything
value: MIGRATION_MASTER_DEPLOY_GAS,
};
const migrationMasterAddress = new ContractDeployer().addressForContract(params);
const isDeployed = tc.isContractDeployed(migrationMasterAddress);

if (await tc.isContractDeployed(migrationMasterAddress)) {
// params.onProgress?.(JettonDeployState.ALREADY_DEPLOYED);
} else {
await contractDeployer.deployContract(params, tonConnection);
// params.onProgress?.(JettonDeployState.AWAITING_MINTER_DEPLOY);
await waitForContractDeploy(migrationMasterAddress, tc);
}

return migrationMasterAddress;
}

export async function createMigrationHelper(
config: MigrationHelperConfig,
tonConnection: TonConnectUI,
owner: Address,
): Promise<Address> {
const contractDeployer = new ContractDeployer();
const tc = await getClient();

// params.onProgress?.(JettonDeployState.BALANCE_CHECK);
const balance = await tc.getBalance(owner);
if (balance.lt(MIGRATION_HELPER_DEPLOY_GAS))
throw new Error("Not enough balance in deployer wallet");
const params = {
code: MIGRATION_HELPER_CODE,
data: await migrationHelperConfigToCell(config),
deployer: owner, //anything
value: MIGRATION_HELPER_DEPLOY_GAS,
};
const migrationHelperAddress = new ContractDeployer().addressForContract(params);
const isDeployed = tc.isContractDeployed(migrationHelperAddress);

if (await tc.isContractDeployed(migrationHelperAddress)) {
// params.onProgress?.(JettonDeployState.ALREADY_DEPLOYED);
} else {
await contractDeployer.deployContract(params, tonConnection);
// params.onProgress?.(JettonDeployState.AWAITING_MINTER_DEPLOY);
await waitForContractDeploy(migrationHelperAddress, tc);
}

return migrationHelperAddress;
}
1 change: 1 addition & 0 deletions src/pages/deployer/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import { offchainFormSpec, onchainFormSpec } from "./data";
import { Form } from "components/form";
import { GithubButton } from "pages/deployer/githubButton";
import { useNavigatePreserveQuery } from "lib/hooks/useNavigatePreserveQuery";
import { getClient } from "lib/get-ton-client";
import { useTonAddress, useTonConnectUI } from "@tonconnect/ui-react";

const DEFAULT_DECIMALS = 9;
Expand Down
66 changes: 66 additions & 0 deletions src/pages/jetton/dataRow/token/Token.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ import brokenImage from "assets/icons/question.png";
import { AppButton } from "components/appButton";
import pen from "assets/icons/pen.svg";
import { CenteringWrapper } from "components/footer/styled";
import { MigrationPopup } from "pages/jetton/migration";
import { UserMigrationPopup } from "pages/jetton/userMigration";
import { useNavigate } from "react-router-dom";

export const Token = () => {
const {
Expand All @@ -45,8 +48,20 @@ export const Token = () => {
jettonLoading,
decimals,
isImageBroken,
isCodeOld,
selectedWalletAddress,
isNewMinterDeployed,
isMigrationMasterDeployed,
mintedJettonsToMaster,
newMinterAddress,
migrationId,
} = useJettonStore();
const [openEdit, setOpenEdit] = useState(false);
const [openMigrationPopup, setOpenMigrationPopup] = useState(false);
const [openUserMigrationPopup, setOpenUserMigrationPopup] = useState(false);
const navigate = useNavigate();

const isMigrationRoute: boolean = !!migrationId;

return (
<StyledBlock sx={{ width: "calc(55% - 15px)" }}>
Expand Down Expand Up @@ -103,6 +118,57 @@ export const Token = () => {
</AppButton>
</Box>
)}
{isMigrationRoute && !jettonLoading && (
<Box sx={{ alignSelf: "start" }}>
<AppButton
width={113}
height={32}
transparent
onClick={() => setOpenUserMigrationPopup(true)}>
<CenteringWrapper>
<img
src={pen}
alt="Migration Icon"
width={15}
height={15}
style={{ marginRight: 4 }}
/>
Migration
</CenteringWrapper>
</AppButton>
</Box>
)}
<UserMigrationPopup
open={openUserMigrationPopup}
setOpen={setOpenUserMigrationPopup}
jettonMinter={jettonMaster!}
migrationMaster={migrationId!}
/>
<MigrationPopup open={openMigrationPopup} setOpen={setOpenMigrationPopup} />
{!isMigrationRoute &&
isCodeOld &&
!jettonLoading &&
selectedWalletAddress &&
!(isNewMinterDeployed && isMigrationMasterDeployed && mintedJettonsToMaster) && (
<Box sx={{ alignSelf: "start" }}>
<AppButton
width={113}
height={32}
transparent
onClick={() => setOpenMigrationPopup(true)}>
<CenteringWrapper>
<img
src={pen}
alt="Pen Icon"
width={15}
height={15}
style={{ marginRight: 4 }}
/>
Migration
</CenteringWrapper>
</AppButton>
</Box>
)}
</StyledTop>
{!isAdmin && isJettonDeployerFaultyOnChainData && (
<Alert variant="filled" severity="error">
Expand Down
Loading