diff --git a/modules/sdk-coin-ton/src/lib/constants.ts b/modules/sdk-coin-ton/src/lib/constants.ts index 08e30f21a6..3d0ba69bcb 100644 --- a/modules/sdk-coin-ton/src/lib/constants.ts +++ b/modules/sdk-coin-ton/src/lib/constants.ts @@ -2,3 +2,5 @@ export const VESTING_CONTRACT_WALLET_ID = 268; export const WALLET_ID = 698983191; export const JETTON_TRANSFER_OPCODE = 0x0f8a7ea5; export const WITHDRAW_OPCODE = '00001000'; +export const VESTING_CONTRACT_CODE_B64 = + 'te6cckECHAEAA/sAART/APSkE/S88sgLAQIBIAISAgFIAwUDrNBsIiDXScFgkVvgAdDTAwFxsJFb4PpAMNs8AdMf0z/4S1JAxwUjghCnczrNurCOpGwS2zyCEPdzOs0BcIAYyMsFUATPFiP6AhPLassfyz/JgED7AOMOExQEAc74SlJAxwUDghByWKabuhOwjtGOLAH6QH/IygAC+kQByMoHy//J0PhEECOBAQj0QfhkINdKwgAglQHUMNAB3rMS5oIQ8limmzJwgBjIywVQBM8WI/oCE8tqyx/LP8mAQPsA2zySXwPiGwIBIAYPAgEgBwoCAW4ICQAZrc52omhAIGuQ64X/wAAZrx32omhAEGuQ64WPwAIBYgsMAUutNG2eNvwiRw1AgIR6STfSmRDOaQPp/5g3gSgBt4EBSJhxWfMYQBMCAWoNDgAPol+1E0NcLH4BL6LHbPPpEAcjKB8v/ydD4RIEBCPQKb6ExhMCASAQEQEpukYts8+EX4RvhH+Ej4SfhK+Ev4RIEwINuYRts82zyBMVA7jygwjXGCDTH9Mf0x8C+CO78mTtRNDTH9Mf0/8wWrryoVAzuvKiAvkBQDP5EPKj+ADbPCDXSsABjpntRO1F7UeRW+1n7WXtZI6C2zztQe3xAfL/kTDi+EGk+GHbPBMUGwB+7UTQ0x8B+GHTHwH4YtP/Afhj9AQB+GTUAdDTPwH4ZdMfAfhm0x8B+GfTHwH4aPoAAfhp+kAB+Gr6QAH4a9HRAlzTB9TR+CPbPCDCAI6bIsAD8uBkIdDTA/pAMfpA+EpSIMcFs5JfBOMNkTDiAfsAFRYAYPhF+EagUhC8kjBw4PhF+EigUhC5kzD4SeD4SfhJ+EUTofhHqQT4RvhHqQQQI6mEoQP6IfpEAcjKB8v/ydD4RIEBCPQKb6Exj18zAXKwwALy4GUB+gAxcdch+gAx+gAx0z8x0x8x0wABwADy4GbTAAGT1DDQ3iFx2zyOKjHTHzAgghBOc3RLuiGCEEdldCS6sSGCEFZ0Q3C6sQGCEFZvdGW6sfLgZ+MOcJJfA+IgwgAYFxoC6gFw2zyObSDXScIAjmPTHyHAACKDC7qxIoEQAbqxIoIQR9VDkbqxIoIQWV8HvLqxIoIQafswbLqxIoIQVm90ZbqxIoIQVnRDcLqx8uBnAcAAIddJwgCwjhXTBzAgwGQhwHexIcBEsQHAV7Hy4GiRMOKRMOLjDRgZAEQB+kQBw/+SW3DgAfgzIG6SW3Dg0CDXSYMHuZJbcODXC/+6ABrTHzCCEFZvdGW68uBnAA6TcvsCkTDiAGb4SPhH+Eb4RcjLP8sfyx/LH/hJ+gL4Ss8W+EvPFsn4RPhD+EL4QcjLH8sfy//0AMzJ7VSo1+S9'; diff --git a/modules/sdk-coin-ton/src/lib/iface.ts b/modules/sdk-coin-ton/src/lib/iface.ts index 4dc33c68d9..7c24021ee5 100644 --- a/modules/sdk-coin-ton/src/lib/iface.ts +++ b/modules/sdk-coin-ton/src/lib/iface.ts @@ -20,3 +20,15 @@ export interface TxData { } export type TransactionExplanation = ITransactionExplanation; + +export type VestingContractParams = { + subWalletId: number; + publicKeyHex: string; + vestingStartTime: number; + vestingTotalDuration: number; + unlockPeriod: number; + cliffDuration: number; + vestingTotalAmount: bigint; + vestingSenderAddress: string; + ownerAddress: string; +}; diff --git a/modules/sdk-coin-ton/src/lib/utils.ts b/modules/sdk-coin-ton/src/lib/utils.ts index 2461f2f9ea..686854ee3a 100644 --- a/modules/sdk-coin-ton/src/lib/utils.ts +++ b/modules/sdk-coin-ton/src/lib/utils.ts @@ -1,6 +1,8 @@ -import { BaseUtils, isValidEd25519PublicKey } from '@bitgo/sdk-core'; import TonWeb from 'tonweb'; - +import { BN } from 'bn.js'; +import { BaseUtils, isValidEd25519PublicKey } from '@bitgo/sdk-core'; +import { VESTING_CONTRACT_CODE_B64 } from './constants'; +import { VestingContractParams } from './iface'; export class Utils implements BaseUtils { /** @inheritdoc */ isValidAddress(address: string): boolean { @@ -85,6 +87,30 @@ export class Utils implements BaseUtils { const address = slice.loadAddress(); return address.toString(); } + + async getVestingContractAddress(params: VestingContractParams): Promise { + const cell = new TonWeb.boc.Cell(); + cell.bits.writeUint(0, 32); //seq no + cell.bits.writeUint(params.subWalletId, 32); + cell.bits.writeBytes(TonWeb.utils.hexToBytes(params.publicKeyHex)); + cell.bits.writeUint(0, 1); // empty whitelist + const vestingParamsCell = new TonWeb.boc.Cell(); + vestingParamsCell.bits.writeUint(params.vestingStartTime, 64); + vestingParamsCell.bits.writeUint(params.vestingTotalDuration, 32); + vestingParamsCell.bits.writeUint(params.unlockPeriod, 32); + vestingParamsCell.bits.writeUint(params.cliffDuration, 32); + vestingParamsCell.bits.writeCoins(new BN(params.vestingTotalAmount.toString())); + const senderAddress = new TonWeb.Address(params.vestingSenderAddress); + vestingParamsCell.bits.writeAddress(senderAddress); + const ownerAddress = new TonWeb.Address(params.ownerAddress); + vestingParamsCell.bits.writeAddress(ownerAddress); + cell.refs.push(vestingParamsCell); + const contractCodeCell = TonWeb.boc.Cell.oneFromBoc(TonWeb.utils.base64ToBytes(VESTING_CONTRACT_CODE_B64)); + const stateInit = TonWeb.Contract.createStateInit(contractCodeCell, cell); + const stateInitHash = await stateInit.hash(); + const contractAddress = new TonWeb.Address('0:' + TonWeb.utils.bytesToHex(stateInitHash)); + return contractAddress.toString(true, true, true); + } } const DUMMY_PRIVATE_KEY = '43e8594854cb53947c4a1a2fab926af11e123f6251dcd5bd0dfb100604186430'; // This dummy private key is used only for fee estimation diff --git a/modules/sdk-coin-ton/test/unit/ton.ts b/modules/sdk-coin-ton/test/unit/ton.ts index b6af2f12c5..84847939a5 100644 --- a/modules/sdk-coin-ton/test/unit/ton.ts +++ b/modules/sdk-coin-ton/test/unit/ton.ts @@ -532,6 +532,22 @@ describe('TON:', function () { '0:d57b25093b1dc09d91962ed13497e571bfb73bb939c3f0e5ed08585a3be9ef6a' ); }); + + it('should get valid vesting contract address', async function () { + const vestingParams = { + subWalletId: 698983191, + publicKeyHex: '71013394862995cfb527dea9cadcc84d66d1197d2a4454579b93a08223e4b309', + vestingStartTime: 1760999164, + vestingTotalDuration: 60 * 60 * 24 * 30, + unlockPeriod: 60 * 60 * 24, + cliffDuration: 60 * 60, + vestingTotalAmount: BigInt('400000000'), + vestingSenderAddress: '0QDOEyzC6KXiVh2Rco2bapH96Vn6lb9YWxgkTVWtq-9CXFtp', + ownerAddress: '0QDBrOgjQThbN7eWBHErRhrJQH3ApecGDtu3K11Lujjmx49Z', + }; + const vestingContractAddress = await utils.getVestingContractAddress(vestingParams); + vestingContractAddress.should.equal('EQAjmgMbwU3WVK_pn2gj1XBG_NMvrkoT4earvs_C9Q6fs-SE'); + }); }); describe('getAddress', function () {