From 8d6b5ef9ddfbb3fbea073e04f236e4db9f596274 Mon Sep 17 00:00:00 2001 From: saimeunt Date: Mon, 7 Oct 2024 23:24:49 +0200 Subject: [PATCH] Launchpad pump with Solidity (#170) --- onchain/cairo/src/launchpad/launchpad.cairo | 188 +++---- .../src/launchpad/LaunchpadPumpDualVM.sol | 517 +++++++++++------- onchain/tests/PumpDualVM.test.ts | 502 +++++++++++++---- 3 files changed, 834 insertions(+), 373 deletions(-) diff --git a/onchain/cairo/src/launchpad/launchpad.cairo b/onchain/cairo/src/launchpad/launchpad.cairo index 190436a2..3a3e500a 100644 --- a/onchain/cairo/src/launchpad/launchpad.cairo +++ b/onchain/cairo/src/launchpad/launchpad.cairo @@ -309,10 +309,10 @@ pub mod LaunchpadMarketplace { self.accesscontrol.assert_only_role(ADMIN_ROLE); // self.ownable.assert_only_owner(); self.address_jediswap_factory_v2.write(address_jediswap_factory_v2); - self - .emit( - SetJediwapV2Factory { address_jediswap_factory_v2: address_jediswap_factory_v2 } - ); + // self + // .emit( + // SetJediwapV2Factory { address_jediswap_factory_v2: address_jediswap_factory_v2 } + // ); } fn set_address_jediswap_nft_router_v2( @@ -320,12 +320,12 @@ pub mod LaunchpadMarketplace { ) { self.accesscontrol.assert_only_role(ADMIN_ROLE); self.address_jediswap_nft_router_v2.write(address_jediswap_nft_router_v2); - self - .emit( - SetJediwapNFTRouterV2 { - address_jediswap_nft_router_v2: address_jediswap_nft_router_v2 - } - ); + // self + // .emit( + // SetJediwapNFTRouterV2 { + // address_jediswap_nft_router_v2: address_jediswap_nft_router_v2 + // } + // ); } fn set_exchanges_address( @@ -573,28 +573,26 @@ pub mod LaunchpadMarketplace { // 80% bought by others // TODO finish test and fix - if pool_coin.liquidity_raised >= threshold { - self - .emit( - LiquidityCanBeAdded { - pool: pool_coin.token_address.clone(), - asset: pool_coin.token_address.clone(), - quote_token_address: pool_coin.token_quote.token_address.clone(), - } - ); + if pool_coin.liquidity_raised >= threshold {// self + // .emit( + // LiquidityCanBeAdded { + // pool: pool_coin.token_address.clone(), + // asset: pool_coin.token_address.clone(), + // quote_token_address: pool_coin.token_quote.token_address.clone(), + // } + // ); // self._add_liquidity(coin_address, SupportedExchanges::Jediswap); } - if mc >= threshold_mc { - // println!("mc >= threshold_mc"); - self - .emit( - LiquidityCanBeAdded { - pool: pool_coin.token_address.clone(), - asset: pool_coin.token_address.clone(), - quote_token_address: pool_coin.token_quote.token_address.clone(), - } - ); + if mc >= threshold_mc {// println!("mc >= threshold_mc"); + // self + // .emit( + // LiquidityCanBeAdded { + // pool: pool_coin.token_address.clone(), + // asset: pool_coin.token_address.clone(), + // quote_token_address: pool_coin.token_quote.token_address.clone(), + // } + // ); // self._add_liquidity(coin_address, SupportedExchanges::Jediswap); } @@ -602,21 +600,20 @@ pub mod LaunchpadMarketplace { // Update state self.shares_by_users.write((get_caller_address(), coin_address), share_user.clone()); self.launched_coins.write(coin_address, pool_coin.clone()); - - self - .emit( - BuyToken { - caller: get_caller_address(), - token_address: coin_address, - amount: amount, - price: total_price, - protocol_fee: amount_protocol_fee, - // creator_fee: 0, - last_price: old_price, - timestamp: get_block_timestamp(), - quote_amount: quote_amount - } - ); + // self + // .emit( + // BuyToken { + // caller: get_caller_address(), + // token_address: coin_address, + // amount: amount, + // price: total_price, + // protocol_fee: amount_protocol_fee, + // // creator_fee: 0, + // last_price: old_price, + // timestamp: get_block_timestamp(), + // quote_amount: quote_amount + // } + // ); } @@ -706,20 +703,19 @@ pub mod LaunchpadMarketplace { .shares_by_users .write((get_caller_address(), coin_address.clone()), share_user.clone()); self.launched_coins.write(coin_address.clone(), pool_update.clone()); - - self - .emit( - SellToken { - caller: get_caller_address(), - key_user: coin_address, - amount: quote_amount, - price: total_price, - protocol_fee: amount_protocol_fee, - creator_fee: amount_creator_fee, - timestamp: get_block_timestamp(), - last_price: old_price, - } - ); + // self + // .emit( + // SellToken { + // caller: get_caller_address(), + // key_user: coin_address, + // amount: quote_amount, + // price: total_price, + // protocol_fee: amount_protocol_fee, + // creator_fee: amount_creator_fee, + // timestamp: get_block_timestamp(), + // last_price: old_price, + // } + // ); } @@ -868,17 +864,17 @@ pub mod LaunchpadMarketplace { self.array_coins.write(total_token, token); } - self - .emit( - CreateToken { - caller: get_caller_address(), - token_address: token_address, - symbol: symbol, - name: name, - initial_supply, - total_supply: initial_supply.clone(), - } - ); + // self + // .emit( + // CreateToken { + // caller: get_caller_address(), + // token_address: token_address, + // symbol: symbol, + // name: name, + // initial_supply, + // total_supply: initial_supply.clone(), + // } + // ); token_address } @@ -976,20 +972,19 @@ pub mod LaunchpadMarketplace { self.total_launch.write(total_launch + 1); self.array_launched_coins.write(total_launch, launch_token_pump); } - - self - .emit( - CreateLaunch { - caller: get_caller_address(), - token_address: coin_address, - amount: 0, - price: initial_key_price, - total_supply: total_supply, - slope: slope, - threshold_liquidity: threshold, - quote_token_address: quote_token_address, - } - ); + // self + // .emit( + // CreateLaunch { + // caller: get_caller_address(), + // token_address: coin_address, + // amount: 0, + // price: initial_key_price, + // total_supply: total_supply, + // slope: slope, + // threshold_liquidity: threshold, + // quote_token_address: quote_token_address, + // } + // ); } // TODO add liquidity to Ekubo, Jediswap and others exchanges enabled @@ -1089,20 +1084,19 @@ pub mod LaunchpadMarketplace { }; let (token_id, _, _, _) = nft_router.mint(mint_params); - - // TODO Locked LP token - - self - .emit( - LiquidityCreated { - id: token_id, - pool: pool, - quote_token_address: quote_token_address, - // token_id:token_id, - owner: launch.owner, - asset: asset_token_address, - } - ); + // TODO Locked LP token + + // self + // .emit( + // LiquidityCreated { + // id: token_id, + // pool: pool, + // quote_token_address: quote_token_address, + // // token_id:token_id, + // owner: launch.owner, + // asset: asset_token_address, + // } + // ); } else { // TODO // Increase liquidity of this pool. } diff --git a/onchain/solidity_contracts/src/launchpad/LaunchpadPumpDualVM.sol b/onchain/solidity_contracts/src/launchpad/LaunchpadPumpDualVM.sol index 24e99e6e..b531ce09 100644 --- a/onchain/solidity_contracts/src/launchpad/LaunchpadPumpDualVM.sol +++ b/onchain/solidity_contracts/src/launchpad/LaunchpadPumpDualVM.sol @@ -8,32 +8,14 @@ using CairoLib for uint256; contract LaunchpadPumpDualVM { /// @dev The address of the cairo contract to call uint256 immutable starknetLaunchpad; - /// @dev The address of the kakarot starknet contract to call uint256 immutable kakarot; - /// @dev The cairo function selector to call - `create_token` - uint256 constant FUNCTION_SELECTOR_CREATE_TOKEN = uint256(keccak256("create_token")) % 2 ** 250; - - /// @dev The cairo function selector to call - `create_and_launch_token` - uint256 constant FUNCTION_SELECTOR_CREATE_TOKEN_AND_LAUNCH= uint256(keccak256("create_and_launch_token")) % 2 ** 250; - - - /// @dev The cairo function selector to call - `launch_token` - uint256 constant FUNCTION_SELECTOR_LAUNCH_TOKEN = uint256(keccak256("launch_token")) % 2 ** 250; - - - /// @dev The cairo function selector to call - `get_coin_launch` - uint256 constant FUNCTION_SELECTOR_GET_LAUNCH = uint256(keccak256("get_coin_launch")) % 2 ** 250; - - /// @dev The cairo function selector to call - `buy_coin_by_quote_amount` - uint256 constant FUNCTION_SELECTOR_BUY_COIN = uint256(keccak256("buy_coin_by_quote_amount")) % 2 ** 250; - - /// @dev The cairo function selector to call - `sell_coin` - uint256 constant FUNCTION_SELECTOR_SELL_COIN = uint256(keccak256("sell_coin")) % 2 ** 250; + mapping (address => uint256) evmToStarknetAddresses; + mapping (uint256 => address) starknetToEvmAddresses; struct SharesTokenUser { - address owner; + address owner; address token_address; uint256 price; uint256 amount_owned; @@ -43,207 +25,376 @@ contract LaunchpadPumpDualVM { uint64 created_at; } + struct CairoTokenQuoteBuyCoin { + uint256 tokenAddress; + uint256 initialKeyPriceLow; + uint256 initialKeyPriceHigh; + uint256 priceLow; + uint256 priceHigh; + uint256 stepIncreaseLinearLow; + uint256 stepIncreaseLinearHigh; + bool isEnable; + } + + struct CairoTokenLaunch { + uint256 owner; + uint256 tokenAddress; + uint256 initialKeyPriceLow; + uint256 initialKeyPriceHigh; + uint256 priceLow; + uint256 priceHigh; + uint256 availableSupplyLow; + uint256 availableSupplyHigh; + uint256 initialPoolSupplyLow; + uint256 initialPoolSupplyHigh; + uint256 totalSupplyLow; + uint256 totalSupplyHigh; + uint256 bondingCurveType; + BondingCurve bondingCurve; + uint64 createdAt; + CairoTokenQuoteBuyCoin tokenQuote; + uint256 liquidityRaisedLow; + uint256 liquidityRaisedHigh; + uint256 tokenHoldedLow; + uint256 tokenHoldedHigh; + bool isLiquidityLaunch; + uint256 slopeLow; + uint256 slopeHigh; + uint256 thresholdLiquidityLow; + uint256 thresholdLiquidityHigh; + } + + struct TokenQuoteBuyCoin { + uint256 tokenAddress; + uint256 initialKeyPrice; + uint256 price; + uint256 stepIncreaseLinear; + bool isEnable; + } + + enum BondingCurve { + Linear, + Trapezoidal, + Scoring, + Exponential, + Limited + } + struct TokenLaunch { address owner; - address token_address; + uint256 tokenAddress; + uint256 initialKeyPrice; uint256 price; - uint256 available_supply; - uint256 total_supply; - uint256 initial_key_price; - uint256 liquidity_raised; - uint256 token_holded; - bool is_liquidity_launch; - uint256 slop; - uint64 created_at; + uint256 availableSupply; + uint256 initialPoolSupply; + uint256 totalSupply; + BondingCurve bondingCurve; + uint64 createdAt; + TokenQuoteBuyCoin tokenQuote; + uint256 liquidityRaised; + uint256 tokenHolded; + bool isLiquidityLaunch; + uint256 slope; + uint256 thresholdLiquidity; + } + + struct CairoToken { + uint256 owner; + uint256 tokenAddress; + uint256 symbol; + uint256 name; + uint256 totalSupplyLow; + uint256 totalSupplyHigh; + uint256 initialSupplyLow; + uint256 initialSupplyHigh; + TokenType tokenType; + uint64 createdAt; + } + + enum TokenType { + ERC20, + ERC404 } struct Token { address owner; - address token_address; - bytes symbol; - bytes name; - uint256 total_supply; - uint256 initial_supply; - uint64 created_at; - + uint256 tokenAddress; + bytes31 symbol; + bytes31 name; + uint256 totalSupply; + uint256 initialSupply; + TokenType tokenType; + uint64 createdAt; } - constructor( - uint256 _kakarot, - uint256 _starknetLaunchpad) { + event CreateToken( + address indexed caller, + uint256 indexed tokenAddress, + bytes31 symbol, + bytes31 name, + uint256 initialSupply, + uint256 totalSupply + ); + + event CreateLaunch( + address indexed caller, + uint256 indexed tokenAddress, + uint256 indexed quoteTokenAddress, + uint256 amount, + uint256 price, + uint256 totalSupply, + uint256 slope, + uint256 thresholdLiquidity + ); + + constructor(uint256 _kakarot, uint256 _starknetLaunchpad) { kakarot = _kakarot; starknetLaunchpad = _starknetLaunchpad; } - function getLaunchPump(uint256 tokenAddress) public { - - uint256[] memory tokenAddressCalldata = new uint256[](1); - tokenAddressCalldata[0] = uint256(uint160(tokenAddress)); - uint256 tokenStarknetAddress = - abi.decode(starknetLaunchpad.staticcallCairo("compute_starknet_address", tokenAddressCalldata), (uint256)); - - // call launch that sent struct - // todo how do it? + function bytes31ToUint256(bytes31 data) internal pure returns(uint256) { + return uint256(bytes32(data) >> 8); } + function uint256ToU256(uint256 data) internal pure returns(uint256, uint256) { + uint128 low = uint128(data); + uint128 high = uint128(data >> 128); + return (low, high); + } - function bytesToUint256(bytes calldata b) internal pure returns (uint256) { - require(b.length <= 32, "Byte array should be no longer than 32 bytes"); - return abi.decode(abi.encodePacked(new bytes(32 - b.length), b), (uint256)); + function uint256ToBytes31(uint256 data) internal pure returns(bytes31 result) { + assembly { + result := shl(8, data) + } } - function bytesFeltToUint256(bytes calldata b) internal pure returns (uint256) { - require(b.length <= 31, "Byte array should be no longer than 31 bytes"); - return abi.decode(abi.encodePacked(new bytes(32 - b.length), b), (uint256)); + function getAllCoins() public view returns(Token[] memory) { + bytes memory returnData = starknetLaunchpad.staticcallCairo("get_all_coins"); + bytes32 offset = 0x0000000000000000000000000000000000000000000000000000000000000020; + CairoToken[] memory cairoTokens = abi.decode( + bytes.concat(offset, returnData), + (CairoToken[]) + ); + Token[] memory tokens = new Token[](cairoTokens.length); + for (uint256 i = 0; i < cairoTokens.length; ++i) { + tokens[i].owner = starknetToEvmAddresses[cairoTokens[i].owner]; + tokens[i].tokenAddress = cairoTokens[i].tokenAddress; + tokens[i].symbol = uint256ToBytes31(cairoTokens[i].symbol); + tokens[i].name = uint256ToBytes31(cairoTokens[i].name); + tokens[i].totalSupply = cairoTokens[i].totalSupplyLow + + (cairoTokens[i].totalSupplyHigh << 128); + tokens[i].initialSupply = cairoTokens[i].initialSupplyLow + + (cairoTokens[i].initialSupplyHigh << 128); + tokens[i].tokenType = cairoTokens[i].tokenType; + tokens[i].createdAt = cairoTokens[i].createdAt; + } + return tokens; } + function getCoinLaunch(uint256 tokenAddress) public view returns(TokenLaunch memory) { + uint256[] memory getCoinLaunchCallData = new uint256[](1); + getCoinLaunchCallData[0] = tokenAddress; + bytes memory returnData = starknetLaunchpad.staticcallCairo( + "get_coin_launch", + getCoinLaunchCallData + ); + CairoTokenLaunch memory cairoTokenLaunch = abi.decode(returnData, (CairoTokenLaunch)); + return TokenLaunch({ + owner: starknetToEvmAddresses[cairoTokenLaunch.owner], + tokenAddress: cairoTokenLaunch.tokenAddress, + initialKeyPrice: cairoTokenLaunch.initialKeyPriceLow + + (cairoTokenLaunch.initialKeyPriceHigh << 128), + price: cairoTokenLaunch.priceLow + (cairoTokenLaunch.priceHigh << 128), + availableSupply: cairoTokenLaunch.availableSupplyLow + + (cairoTokenLaunch.availableSupplyHigh << 128), + initialPoolSupply: cairoTokenLaunch.initialPoolSupplyLow + + (cairoTokenLaunch.initialPoolSupplyHigh << 128), + totalSupply: cairoTokenLaunch.totalSupplyLow + + (cairoTokenLaunch.totalSupplyHigh << 128), + bondingCurve: cairoTokenLaunch.bondingCurve, + createdAt: cairoTokenLaunch.createdAt, + tokenQuote: TokenQuoteBuyCoin({ + tokenAddress: cairoTokenLaunch.tokenQuote.tokenAddress, + initialKeyPrice: cairoTokenLaunch.tokenQuote.initialKeyPriceLow + + (cairoTokenLaunch.tokenQuote.initialKeyPriceHigh << 128), + price: cairoTokenLaunch.tokenQuote.priceLow + + (cairoTokenLaunch.tokenQuote.priceHigh << 128), + stepIncreaseLinear: cairoTokenLaunch.tokenQuote.stepIncreaseLinearLow + + (cairoTokenLaunch.tokenQuote.stepIncreaseLinearHigh << 128), + isEnable: cairoTokenLaunch.tokenQuote.isEnable + }), + liquidityRaised: cairoTokenLaunch.liquidityRaisedLow + + (cairoTokenLaunch.liquidityRaisedHigh << 128), + tokenHolded: cairoTokenLaunch.tokenHoldedLow + + (cairoTokenLaunch.tokenHoldedHigh << 128), + isLiquidityLaunch: cairoTokenLaunch.isLiquidityLaunch, + slope: cairoTokenLaunch.slopeLow + (cairoTokenLaunch.slopeHigh << 128), + thresholdLiquidity: cairoTokenLaunch.thresholdLiquidityLow + + (cairoTokenLaunch.thresholdLiquidityHigh << 128) + }); + } - /** */ - function createToken(address recipient, - bytes calldata symbol, - bytes calldata name, - uint256 initialSupply, - bytes calldata contractAddressSalt + function createToken( + address recipient, + bytes31 symbol, + bytes31 name, + uint256 initialSupply, + bytes31 contractAddressSalt ) public { - - uint256[] memory recipientAddressCalldata = new uint256[](1); - recipientAddressCalldata[0] = uint256(uint160(recipient)); - - uint256 recipientStarknetAddress = - abi.decode(kakarot.staticcallCairo("compute_starknet_address", recipientAddressCalldata), (uint256)); - - uint128 amountLow = uint128(initialSupply); - uint128 amountHigh = uint128(initialSupply >> 128); - // Decode the first 32 bytes (a uint256 is 32 bytes) - uint256 symbolAsUint = bytesFeltToUint256(symbol); - uint256 nameAsUint = bytesFeltToUint256(name); - uint256 contractAddressSaltResult = bytesFeltToUint256(contractAddressSalt); - - // uint256 symbolAsUint = convertBytesToUint256(symbol); - // uint256 nameAsUint = convertBytesToUint256(name); - // uint256 contractAddressSaltResult = convertBytesToUint256(contractAddressSalt); - + // Get owner + if (evmToStarknetAddresses[msg.sender] == 0) { + uint256[] memory ownerAddressCalldata = new uint256[](1); + ownerAddressCalldata[0] = uint256(uint160(msg.sender)); + uint256 ownerStarknetAddress = abi.decode( + kakarot.staticcallCairo("compute_starknet_address", ownerAddressCalldata), + (uint256) + ); + evmToStarknetAddresses[msg.sender] = ownerStarknetAddress; + starknetToEvmAddresses[ownerStarknetAddress] = msg.sender; + } + // Get recipient + if (evmToStarknetAddresses[recipient] == 0) { + uint256[] memory recipientAddressCalldata = new uint256[](1); + recipientAddressCalldata[0] = uint256(uint160(recipient)); + uint256 recipientStarknetAddress = abi.decode( + kakarot.staticcallCairo("compute_starknet_address", recipientAddressCalldata), + (uint256) + ); + evmToStarknetAddresses[recipient] = recipientStarknetAddress; + starknetToEvmAddresses[recipientStarknetAddress] = recipient; + } uint256[] memory createTokenCallData = new uint256[](6); - createTokenCallData[0] = recipientStarknetAddress; - createTokenCallData[1] = uint(symbolAsUint); - createTokenCallData[2] = uint(nameAsUint); - createTokenCallData[3] = uint256(amountLow); - createTokenCallData[4] = uint256(amountHigh); - createTokenCallData[5] = uint256(contractAddressSaltResult); - - // TODO change to create token only - starknetLaunchpad.callCairo(FUNCTION_SELECTOR_CREATE_TOKEN, createTokenCallData); - // starknetLaunchpad.callCairo(FUNCTION_SELECTOR_CREATE_TOKEN_AND_LAUNCH, createTokenCallData); - - // starknetLaunchpad.callCairo("create_token", createTokenCallData); - // starknetLaunchpad.staticcallCairo("create_token", createTokenCallData); - + createTokenCallData[0] = evmToStarknetAddresses[recipient]; + createTokenCallData[1] = bytes31ToUint256(symbol); + createTokenCallData[2] = bytes31ToUint256(name); + (createTokenCallData[3], createTokenCallData[4]) = uint256ToU256(initialSupply); + createTokenCallData[5] = bytes31ToUint256(contractAddressSalt); + bytes memory returnData = starknetLaunchpad.delegatecallCairo( + "create_token", + createTokenCallData + ); + uint tokenAddress = abi.decode(returnData, (uint256)); + emit CreateToken( + msg.sender, + tokenAddress, + symbol, + name, + initialSupply, + initialSupply + ); } function createAndLaunchToken( - bytes calldata symbol, - bytes calldata name, - uint256 initialSupply, - bytes calldata contractAddressSalt - ) public { - uint128 amountLow = uint128(initialSupply); - uint128 amountHigh = uint128(initialSupply >> 128); - - uint256[] memory createLaunchTokenCallData = new uint256[](5); - // Decode the first 32 bytes (a uint256 is 32 bytes) - uint256 symbolAsUint = bytesFeltToUint256(symbol); - uint256 nameAsUint = bytesFeltToUint256(name); - uint256 contractAddressSaltResult = bytesFeltToUint256(contractAddressSalt); - createLaunchTokenCallData[0] = uint(symbolAsUint); - createLaunchTokenCallData[1] = uint(nameAsUint); - createLaunchTokenCallData[2] = uint256(amountLow); - createLaunchTokenCallData[3] = uint256(amountHigh); - createLaunchTokenCallData[4] = uint256(contractAddressSaltResult); - - starknetLaunchpad.callCairo(FUNCTION_SELECTOR_CREATE_TOKEN_AND_LAUNCH, createLaunchTokenCallData); - // starknetLaunchpad.staticcallCairo("create_and_launch_token", createLaunchTokenCallData); - - + bytes31 symbol, + bytes31 name, + uint256 initialSupply, + bytes31 contractAddressSalt + ) public { + // Get owner + if (evmToStarknetAddresses[msg.sender] == 0) { + uint256[] memory ownerAddressCalldata = new uint256[](1); + ownerAddressCalldata[0] = uint256(uint160(msg.sender)); + uint256 ownerStarknetAddress = abi.decode( + kakarot.staticcallCairo("compute_starknet_address", ownerAddressCalldata), + (uint256) + ); + evmToStarknetAddresses[msg.sender] = ownerStarknetAddress; + starknetToEvmAddresses[ownerStarknetAddress] = msg.sender; + } + uint256[] memory createLaunchTokenCallData = new uint256[](5); + createLaunchTokenCallData[0] = bytes31ToUint256(symbol); + createLaunchTokenCallData[1] = bytes31ToUint256(name); + (createLaunchTokenCallData[2], createLaunchTokenCallData[3]) = uint256ToU256(initialSupply); + createLaunchTokenCallData[4] = bytes31ToUint256(contractAddressSalt); + bytes memory returnData = starknetLaunchpad.delegatecallCairo( + "create_and_launch_token", + createLaunchTokenCallData + ); + uint tokenAddress = abi.decode(returnData, (uint256)); + TokenLaunch memory tokenLaunch = getCoinLaunch(tokenAddress); + emit CreateToken( + msg.sender, + tokenAddress, + symbol, + name, + initialSupply, + initialSupply + ); + emit CreateLaunch( + msg.sender, + tokenAddress, + tokenLaunch.tokenQuote.tokenAddress, + 0, + tokenLaunch.initialKeyPrice, + tokenLaunch.totalSupply, + tokenLaunch.slope, + tokenLaunch.thresholdLiquidity + ); } // Launch a token already deployed // Need to be approve or transfer to the launchpad - function launchToken( - address coinAddress - ) public { - - uint256[] memory coinAddressCalldata = new uint256[](1); - coinAddressCalldata[0] = uint256(uint160(coinAddress)); - uint256 coinStarknetAddress = - abi.decode(kakarot.staticcallCairo("compute_starknet_address", coinAddressCalldata), (uint256)); - + function launchToken(uint256 tokenAddress) public { uint256[] memory launchTokenCalldata = new uint256[](1); - launchTokenCalldata[0] = coinStarknetAddress; - - starknetLaunchpad.callCairo(FUNCTION_SELECTOR_LAUNCH_TOKEN, launchTokenCalldata); - + launchTokenCalldata[0] = tokenAddress; + starknetLaunchpad.delegatecallCairo("launch_token", launchTokenCalldata); + TokenLaunch memory tokenLaunch = getCoinLaunch(tokenAddress); + emit CreateLaunch( + msg.sender, + tokenAddress, + tokenLaunch.tokenQuote.tokenAddress, + 0, + tokenLaunch.initialKeyPrice, + tokenLaunch.totalSupply, + tokenLaunch.slope, + tokenLaunch.thresholdLiquidity + ); } - function buyToken( - address coinAddress - ) public { - uint256[] memory coinAddressCalldata = new uint256[](1); - coinAddressCalldata[0] = uint256(uint160(coinAddress)); - uint256 coinStarknetAddress = - abi.decode(kakarot.staticcallCairo("compute_starknet_address", coinAddressCalldata), (uint256)); - - uint256[] memory buyTokenCalldata = new uint256[](1); - buyTokenCalldata[0] = coinStarknetAddress; - starknetLaunchpad.callCairo(FUNCTION_SELECTOR_BUY_COIN, buyTokenCalldata); - + function buyTokenByQuoteAmount(uint256 tokenAddress, uint256 quoteAmount) public { + uint256[] memory buyTokenByQuoteAmountCalldata = new uint256[](3); + buyTokenByQuoteAmountCalldata[0] = tokenAddress; + (buyTokenByQuoteAmountCalldata[1], buyTokenByQuoteAmountCalldata[2]) = + uint256ToU256(quoteAmount); + starknetLaunchpad.delegatecallCairo( + "buy_coin_by_quote_amount", + buyTokenByQuoteAmountCalldata + ); } - function sellToken( - address coinAddress - ) public { - uint256[] memory coinAddressCalldata = new uint256[](1); - coinAddressCalldata[0] = uint256(uint160(coinAddress)); - uint256 coinStarknetAddress = - abi.decode(kakarot.staticcallCairo("compute_starknet_address", coinAddressCalldata), (uint256)); - - uint256[] memory sellTokenCalldata = new uint256[](1); - sellTokenCalldata[0] = coinStarknetAddress; - starknetLaunchpad.callCairo(FUNCTION_SELECTOR_SELL_COIN, sellTokenCalldata); - + function sellToken(uint256 tokenAddress, uint256 quoteAmount) public { + uint256[] memory sellTokenCalldata = new uint256[](3); + sellTokenCalldata[0] = tokenAddress; + (sellTokenCalldata[1], sellTokenCalldata[2]) = uint256ToU256(quoteAmount); + starknetLaunchpad.delegatecallCairo("sell_coin", sellTokenCalldata); } - // Function to cast `bytes` to `bytes31` - function bytesToBytes31(bytes memory b) internal pure returns (bytes31) { - require(b.length == 31, "Invalid bytes length for bytes31"); - - bytes31 result; - assembly { - result := mload(add(b, 31)) - } - - return result; - } - - // Function to convert `bytes31` to `uint256` - function bytes31ToUint256(bytes31 input) internal pure returns (uint256) { - uint256 result; - assembly { - result := mload(add(input, 31)) // Read 31 bytes as uint256 - } - return result; + function getDefaultToken() public view returns(TokenQuoteBuyCoin memory) { + bytes memory returnData = starknetLaunchpad.staticcallCairo("get_default_token"); + CairoTokenQuoteBuyCoin memory cairoTokenQuoteBuyCoin = abi.decode( + returnData, + (CairoTokenQuoteBuyCoin) + ); + return TokenQuoteBuyCoin({ + tokenAddress: cairoTokenQuoteBuyCoin.tokenAddress, + initialKeyPrice: cairoTokenQuoteBuyCoin.initialKeyPriceLow + + (cairoTokenQuoteBuyCoin.initialKeyPriceHigh << 128), + price: cairoTokenQuoteBuyCoin.priceLow + + (cairoTokenQuoteBuyCoin.priceHigh << 128), + stepIncreaseLinear: cairoTokenQuoteBuyCoin.stepIncreaseLinearLow + + (cairoTokenQuoteBuyCoin.stepIncreaseLinearHigh << 128), + isEnable: cairoTokenQuoteBuyCoin.isEnable + }); } - // Function to convert `bytes calldata` to `bytes31` and then to uint256 - function convertBytesToUint256(bytes calldata input) internal pure returns (uint256) { - require(input.length == 31, "Input must be exactly 31 bytes long"); - - // Convert `bytes calldata` to `bytes31` - bytes31 fixedBytes = bytes31(bytesToBytes31(input)); - - // Convert `bytes31` to `uint256` - uint256 result = bytes31ToUint256(fixedBytes); - - return result; + function setToken(TokenQuoteBuyCoin memory quoteToken) public { + uint256[] memory setTokenCalldata = new uint256[](8); + setTokenCalldata[0] = quoteToken.tokenAddress; + (setTokenCalldata[1], setTokenCalldata[2]) = uint256ToU256(quoteToken.initialKeyPrice); + (setTokenCalldata[3], setTokenCalldata[4]) = uint256ToU256(quoteToken.price); + (setTokenCalldata[5], setTokenCalldata[6]) = uint256ToU256(quoteToken.stepIncreaseLinear); + setTokenCalldata[7] = quoteToken.isEnable ? 1 : 0; + starknetLaunchpad.delegatecallCairo("set_token", setTokenCalldata); } } \ No newline at end of file diff --git a/onchain/tests/PumpDualVM.test.ts b/onchain/tests/PumpDualVM.test.ts index 458df491..ab84b667 100644 --- a/onchain/tests/PumpDualVM.test.ts +++ b/onchain/tests/PumpDualVM.test.ts @@ -1,7 +1,7 @@ import { expect } from "chai"; import hre from "hardhat"; import { ethers } from "hardhat"; -import { parseEther, type Contract as EthContract, type Signer } from "ethers"; +import { parseEther, EventLog, stripZerosLeft, zeroPadValue } from "ethers"; import type { HardhatEthersSigner } from "@nomicfoundation/hardhat-ethers/signers"; import "@nomicfoundation/hardhat-ethers"; import { @@ -10,22 +10,19 @@ import { getTestProvider, getTestAccount, } from "../scripts/config"; -import dotenv from "dotenv"; import { - byteArray, cairo, - constants, DeclareDeployUDCResponse, Contract as StarknetContract, + shortString, } from "starknet"; -import { DualVMToken, LaunchpadPumpDualVM } from "../typechain-types"; import { - formatFloatToUint256, - LAUNCHPAD_ADDRESS, - CLASS_HASH, - TOKENS_ADDRESS, -} from "common"; - + DualVMToken, + DualVMToken__factory, + LaunchpadPumpDualVM, +} from "../typechain-types"; +import { formatFloatToUint256 } from "common"; +import dotenv from "dotenv"; dotenv.config(); const KAKAROT_ADDRESS = process.env.KAKAROT_ADDRESS || ""; @@ -37,17 +34,15 @@ describe("PumpDualVM", function () { this.timeout(0); let pumpVM: LaunchpadPumpDualVM; let owner: HardhatEthersSigner; - let ownerStarknet: string; + let ownerStarknet: bigint; let addr1: HardhatEthersSigner; let addr2: HardhatEthersSigner; - let dualVMToken: DualVMToken; + let DualVMTokenFactory: DualVMToken__factory; + let dualVmQuoteToken: DualVMToken; const starknetLaunchpadSierra = readContractSierra("LaunchpadMarketplace"); const starknetLaunchpadCasm = readContractSierraCasm("LaunchpadMarketplace"); - // const starknetTokenSierra = readContractSierra("DualVmToken"); - // const starknetTokenCasm = readContractSierraCasm("DualVmToken"); - const starknetTokenSierra = readContractSierra("ERC20"); const starknetTokenCasm = readContractSierraCasm("ERC20"); let starknetToken: StarknetContract; @@ -58,45 +53,54 @@ describe("PumpDualVM", function () { let ddToken: DeclareDeployUDCResponse; let starknetLaunchpad: StarknetContract; - const initial_key_price = cairo.uint256(1); - const step_increase_linear = cairo.uint256(1); + const initialKeyPrice = cairo.uint256(1); + const stepIncreaseLinear = cairo.uint256(1); /** TODO check correct format for uint256 */ + // const init_supply_nb = 100_000_000; - // const threshold_liquidity_nb = 10; - // const threshold_liquidity = formatFloatToUint256(threshold_liquidity_nb) - // const threshold_marketcap_nb = 5000; - // const threshold_marketcap = formatFloatToUint256(threshold_marketcap_nb); - const init_supply_nb = 100_000_000; - - const init_supply = formatFloatToUint256(init_supply_nb); - const init_supply_bn = ethers.toBigInt(init_supply_nb); - // // const threshold_marketcap = cairo.uint256(threshold_marketcap_nb); - // // const threshold_liquidity = cairo.uint256(threshold_liquidity_nb); - const TOKEN_QUOTE_ADDRESS = - TOKENS_ADDRESS[constants.StarknetChainId.SN_SEPOLIA].ETH; - const TOKEN_CLASS_HASH = - CLASS_HASH.TOKEN[constants.StarknetChainId.SN_SEPOLIA]; - - // const name_token = cairo.felt("TEST"); - // const symbol_token = cairo.felt("TEST_SYMBOL"); - const name_token = "TEST"; - const symbol_token = "TEST_SYMB"; - // const nameBytes = ethers.toUtf8Bytes(name_token).slice(0, 31) - const nameBytes = ethers.toUtf8Bytes(name_token).slice(0, 31); - const symbolBytes = ethers.toUtf8Bytes(symbol_token).slice(0, 31); - let timestampBytes = ethers - .toUtf8Bytes(new Date().getTime().toString()) - .slice(0, 31); + // const init_supply = formatFloatToUint256(init_supply_nb); + // const init_supply_bn = ethers.toBigInt(init_supply_nb); // const initial_key_price = cairo.uint256(1); // const step_increase_linear = cairo.uint256(1); /** TODO check correct format for uint256 */ - const threshold_liquidity_nb = 10; - const threshold_liquidity = formatFloatToUint256(threshold_liquidity_nb); - const threshold_marketcap_nb = 5000; - const threshold_marketcap = formatFloatToUint256(threshold_marketcap_nb); + // const thresholdLiquidity = formatFloatToUint256(10); + // const thresholdMarketcap = formatFloatToUint256(5000); + const thresholdLiquidity = cairo.uint256(10); + const thresholdMarketcap = cairo.uint256(500); + + const toBytes31 = (str: string) => + zeroPadValue(shortString.encodeShortString(str), 31); + + const fromBytes31 = (bytes31: string) => + shortString.decodeShortString(stripZerosLeft(bytes31)); + + const computeStarknetAddress = async (evmAddress: string) => { + const result = await account.callContract({ + contractAddress: KAKAROT_ADDRESS, + calldata: [evmAddress], + entrypoint: "compute_starknet_address", + }); + return BigInt(result[0]); + }; + + const deployDualVmToken = async (tokenAddress: string) => { + const dualVmToken = await DualVMTokenFactory.deploy( + KAKAROT_ADDRESS, + tokenAddress + ).then((c) => c.waitForDeployment()); + // Whitelist the Dual Token contract to call Cairo contracts + await account.execute([ + { + contractAddress: KAKAROT_ADDRESS, + calldata: [await dualVmToken.getAddress(), true], + entrypoint: "set_authorized_cairo_precompile_caller", + }, + ]); + return dualVmToken; + }; this.beforeAll(async function () { try { @@ -104,13 +108,7 @@ describe("PumpDualVM", function () { // Pre-compute the StarkNet address of the ETH-side owner // to mint him the initial supply of the token - ownerStarknet = ( - await account.callContract({ - contractAddress: KAKAROT_ADDRESS, - calldata: [owner.address], - entrypoint: "compute_starknet_address", - }) - )[0]; + ownerStarknet = await computeStarknetAddress(owner.address); // Send eth to addr1 and addr2, effectively deploying the underlying EOA accounts await owner @@ -134,18 +132,13 @@ describe("PumpDualVM", function () { contract: starknetTokenSierra, casm: starknetTokenCasm, constructorCalldata: [ - cairo.felt("TEST_SYMBOL"), - cairo.felt("TEST"), - cairo.uint256(100_000_000), + cairo.felt("USDC token"), + cairo.felt("USDC"), + cairo.uint256(1_000_000), ownerStarknet, 18, ], }); - // ddToken = await account.declareAndDeploy({ - // contract: starknetTokenSierra, - // casm: starknetTokenCasm, - // constructorCalldata: [100_000_000, 0, ownerStarknet], - // }); starknetToken = new StarknetContract( starknetTokenSierra.abi, @@ -153,6 +146,9 @@ describe("PumpDualVM", function () { account ); + DualVMTokenFactory = await hre.ethers.getContractFactory("DualVMToken"); + dualVmQuoteToken = await deployDualVmToken(starknetToken.address); + // Deploy the starknetLaunchpad console.log("deploy launchpad"); @@ -161,13 +157,12 @@ describe("PumpDualVM", function () { casm: starknetLaunchpadCasm, constructorCalldata: [ ownerStarknet, - initial_key_price, - TOKEN_QUOTE_ADDRESS, - step_increase_linear, + initialKeyPrice, + starknetToken.address, + stepIncreaseLinear, ddToken.declare.class_hash, - // ddToken.declare.class_hash, - threshold_liquidity, - threshold_marketcap, + thresholdLiquidity, + thresholdMarketcap, ], }); @@ -195,51 +190,372 @@ describe("PumpDualVM", function () { entrypoint: "set_authorized_cairo_precompile_caller", }, ]); + // fund addr1 with the dualVmQuoteToken + await dualVmQuoteToken + .transfer(addr1.address, 1000n) + .then((tx) => tx.wait()); } catch (e) { console.log("error before all", e); } }); - describe("createAndLaunchToken", function () { - it("Should create token and launch pool", async function () { - timestampBytes = ethers - .toUtf8Bytes(new Date().getTime().toString()) - .slice(0, 31); + describe("createToken", function () { + it("should create token", async function () { + const receipt = await pumpVM + .createToken( + owner.address, + toBytes31("symbol1"), + toBytes31("name1"), + 100_000_000n, + toBytes31("salty1") + ) + .then((tx) => tx.wait()); + if (!receipt) { + expect.fail("Tx receipt not found"); + } + const [createToken] = receipt.logs as EventLog[]; + const { caller, tokenAddress, symbol, name, initialSupply, totalSupply } = + createToken.args.toObject() as { + caller: string; + tokenAddress: bigint; + symbol: string; + name: string; + initialSupply: bigint; + totalSupply: bigint; + }; + const tokens = await pumpVM.getAllCoins(); + const token = tokens.find( + ({ symbol }) => fromBytes31(symbol) === "symbol1" + ); + if (!token) { + expect.fail("Token not found"); + } + expect({ + owner: token.owner, + tokenAddress: token.tokenAddress, + symbol: token.symbol, + name: token.name, + initialSupply: token.initialSupply, + totalSupply: token.totalSupply, + }).to.include({ + owner: owner.address, + tokenAddress, + symbol: toBytes31("symbol1"), + name: toBytes31("name1"), + initialSupply: 100_000_000n, + totalSupply: 100_000_000n, + }); + expect({ + caller, + tokenAddress, + symbol, + name, + initialSupply, + totalSupply, + }).to.include({ + caller: owner.address, + tokenAddress: token.tokenAddress, + symbol: toBytes31("symbol1"), + name: toBytes31("name1"), + initialSupply: 100_000_000n, + totalSupply: 100_000_000n, + }); + }); + }); - await pumpVM - .connect(addr1) + describe("createAndLaunchToken", function () { + it("should create token and launch pool", async function () { + const receipt = await pumpVM .createAndLaunchToken( - symbolBytes, - nameBytes, - // init_supply_bn, + toBytes31("symbol2"), + toBytes31("name2"), 100_000_000n, - timestampBytes + toBytes31("salty2") ) .then((tx) => tx.wait()); + if (!receipt) { + expect.fail("Tx receipt not found"); + } + const [, createLaunch] = receipt.logs as EventLog[]; + const { + caller, + tokenAddress, + quoteTokenAddress, + amount, + price, + totalSupply, + slope, + thresholdLiquidity, + } = createLaunch.args.toObject() as { + caller: string; + tokenAddress: bigint; + quoteTokenAddress: bigint; + amount: bigint; + price: bigint; + totalSupply: bigint; + slope: bigint; + thresholdLiquidity: bigint; + }; + const starknetTokenLaunch = await starknetLaunchpad.call( + "get_coin_launch", + [tokenAddress] + ); + const tokenLaunch = await pumpVM.getCoinLaunch(tokenAddress); + expect(starknetTokenLaunch).to.deep.include({ + owner: await computeStarknetAddress(tokenLaunch.owner), + token_address: tokenLaunch.tokenAddress, + initial_key_price: tokenLaunch.initialKeyPrice, + price: tokenLaunch.price, + available_supply: tokenLaunch.availableSupply, + initial_pool_supply: tokenLaunch.initialPoolSupply, + total_supply: tokenLaunch.totalSupply, + created_at: tokenLaunch.createdAt, + token_quote: { + token_address: tokenLaunch.tokenQuote.tokenAddress, + initial_key_price: tokenLaunch.tokenQuote.initialKeyPrice, + price: tokenLaunch.tokenQuote.price, + step_increase_linear: tokenLaunch.tokenQuote.stepIncreaseLinear, + is_enable: tokenLaunch.tokenQuote.isEnable, + }, + liquidity_raised: tokenLaunch.liquidityRaised, + token_holded: tokenLaunch.tokenHolded, + is_liquidity_launch: tokenLaunch.isLiquidityLaunch, + slope: tokenLaunch.slope, + threshold_liquidity: tokenLaunch.thresholdLiquidity, + }); + expect({ + caller, + tokenAddress, + quoteTokenAddress, + amount, + price, + totalSupply, + slope, + thresholdLiquidity, + }).to.include({ + caller: owner.address, + tokenAddress: tokenLaunch.tokenAddress, + quoteTokenAddress: tokenLaunch.tokenQuote.tokenAddress, + amount: 0n, + price: tokenLaunch.initialKeyPrice, + totalSupply: tokenLaunch.totalSupply, + slope: tokenLaunch.slope, + thresholdLiquidity: tokenLaunch.thresholdLiquidity, + }); }); }); - /** @TODO fix infinite timeout */ - describe("createToken", function () { - it("Should create token", async function () { - timestampBytes = ethers - .toUtf8Bytes(new Date().getTime().toString()) - .slice(0, 31); + describe("launchToken", function () { + it("should launch token", async function () { + const createTokenReceipt = await pumpVM + .createToken( + owner.address, + toBytes31("symbol3"), + toBytes31("name3"), + 100_000_000n, + toBytes31("salty3") + ) + .then((tx) => tx.wait()); + if (!createTokenReceipt) { + expect.fail("Tx receipt not found"); + } + const [createToken] = createTokenReceipt.logs as EventLog[]; + const { tokenAddress } = createToken.args.toObject() as { + tokenAddress: bigint; + }; + const dualVmToken = await deployDualVmToken( + `0x${tokenAddress.toString(16)}` + ); + await dualVmToken.starknetApprove( + starknetLaunchpad.address, + 100_000_000n + ); + const launchTokenReceipt = await pumpVM + .launchToken(tokenAddress) + .then((tx) => tx.wait()); + if (!launchTokenReceipt) { + expect.fail("Tx receipt not found"); + } + const [createLaunch] = launchTokenReceipt.logs as EventLog[]; + const { + caller, + quoteTokenAddress, + amount, + price, + totalSupply, + slope, + thresholdLiquidity, + } = createLaunch.args.toObject() as { + caller: string; + quoteTokenAddress: bigint; + amount: bigint; + price: bigint; + totalSupply: bigint; + slope: bigint; + thresholdLiquidity: bigint; + }; + const tokenLaunch = await pumpVM.getCoinLaunch(tokenAddress); + expect({ + caller, + tokenAddress, + quoteTokenAddress, + amount, + price, + totalSupply, + slope, + thresholdLiquidity, + }).to.include({ + caller: owner.address, + tokenAddress: tokenLaunch.tokenAddress, + quoteTokenAddress: tokenLaunch.tokenQuote.tokenAddress, + amount: 0n, + price: tokenLaunch.initialKeyPrice, + totalSupply: tokenLaunch.totalSupply, + slope: tokenLaunch.slope, + thresholdLiquidity: tokenLaunch.thresholdLiquidity, + }); + }); + }); + + describe("buyCoinByQuoteAmount", function () { + it("buy coin with address and quote amount", async function () { + const receipt = await pumpVM + .createAndLaunchToken( + toBytes31("symbol4"), + toBytes31("name4"), + 100_000_000n, + toBytes31("salty4") + ) + .then((tx) => tx.wait()); + if (!receipt) { + expect.fail("Tx receipt not found"); + } + const [createToken] = receipt.logs as EventLog[]; + const { tokenAddress } = createToken.args.toObject() as { + tokenAddress: bigint; + }; + const dualVmToken = await deployDualVmToken( + `0x${tokenAddress.toString(16)}` + ); + await dualVmQuoteToken + .connect(addr1) + .starknetApprove(starknetLaunchpad.address, 1000n) + .then((tx) => tx.wait()); + const buyerQuoteTokenBalanceBeforeBuy = await dualVmQuoteToken.balanceOf( + addr1.address + ); + const buyerTokenBalanceBeforeBuy = await dualVmToken.balanceOf( + addr1.address + ); await pumpVM .connect(addr1) - .createToken( - addr2.address, - symbolBytes, - nameBytes, - // init_supply_bn, + .buyTokenByQuoteAmount(tokenAddress, 100n) + .then((tx) => tx.wait()); + const buyerQuoteTokenBalanceAfterBuy = await dualVmQuoteToken.balanceOf( + addr1.address + ); + const buyerTokenBalanceAfterBuy = await dualVmToken.balanceOf( + addr1.address + ); + expect(Number(buyerQuoteTokenBalanceBeforeBuy)).to.be.greaterThanOrEqual( + Number(buyerQuoteTokenBalanceAfterBuy) + ); + expect(Number(buyerTokenBalanceBeforeBuy)).to.be.lessThanOrEqual( + Number(buyerTokenBalanceAfterBuy) + ); + }); + }); + + describe("sellCoin", function () { + it("should sell coin by quote amount", async function () { + const receipt = await pumpVM + .createAndLaunchToken( + toBytes31("symbol5"), + toBytes31("name5"), 100_000_000n, - timestampBytes + toBytes31("salty5") ) .then((tx) => tx.wait()); + if (!receipt) { + expect.fail("Tx receipt not found"); + } + const [createToken] = receipt.logs as EventLog[]; + const { tokenAddress } = createToken.args.toObject() as { + tokenAddress: bigint; + }; + const dualVmToken = await deployDualVmToken( + `0x${tokenAddress.toString(16)}` + ); + await dualVmQuoteToken + .connect(addr1) + .starknetApprove(starknetLaunchpad.address, 1000n) + .then((tx) => tx.wait()); + await pumpVM + .connect(addr1) + .buyTokenByQuoteAmount(tokenAddress, 1n) + .then((tx) => tx.wait()); + const sellerQuoteTokenBalanceBeforeSell = + await dualVmQuoteToken.balanceOf(addr1.address); + const sellerTokenBalanceBeforeSell = await dualVmToken.balanceOf( + addr1.address + ); + await pumpVM + .connect(addr1) + .sellToken(tokenAddress, 1n) + .then((tx) => tx.wait()); + const sellerQuoteTokenBalanceAfterSell = await dualVmQuoteToken.balanceOf( + addr1.address + ); + const sellerTokenBalanceAfterSell = await dualVmToken.balanceOf( + addr1.address + ); + expect(Number(sellerQuoteTokenBalanceBeforeSell)).to.be.lessThanOrEqual( + Number(sellerQuoteTokenBalanceAfterSell) + ); + expect(Number(sellerTokenBalanceBeforeSell)).to.be.greaterThanOrEqual( + Number(sellerTokenBalanceAfterSell) + ); }); }); - describe("buyCoin", function () { - it("Buy coin with address and quote amount", async function () {}); + describe("setToken", function () { + it("should set the default quote token", async function () { + const ddNewQuoteToken = await account.declareAndDeploy({ + contract: starknetTokenSierra, + casm: starknetTokenCasm, + constructorCalldata: [ + cairo.felt("EURe token"), + cairo.felt("EURe"), + cairo.uint256(100_000_000), + ownerStarknet, + 18, + ], + }); + const newQuoteToken = new StarknetContract( + starknetTokenSierra.abi, + ddNewQuoteToken.deploy.contract_address, + account + ); + await pumpVM + .setToken({ + tokenAddress: newQuoteToken.address, + initialKeyPrice: 1n, + price: 1n, + stepIncreaseLinear: 1n, + isEnable: true, + }) + .then((tx) => tx.wait()); + const starknetDefaultToken = await starknetLaunchpad.call( + "get_default_token" + ); + const defaultToken = await pumpVM.getDefaultToken(); + expect(starknetDefaultToken).to.include({ + token_address: defaultToken.tokenAddress, + initial_key_price: defaultToken.initialKeyPrice, + price: defaultToken.price, + step_increase_linear: defaultToken.stepIncreaseLinear, + is_enable: defaultToken.isEnable, + }); + }); }); });