Skip to content

Commit

Permalink
add setup for deploy and test (#119)
Browse files Browse the repository at this point in the history
* initial commit

* set pool_state init params for testing

* add YasMintCallback for testing

* add mint() YASMintCallback

* adding WALLET and POOL_ADDRESS constants

* remove prints() from yas_pool

* add caller into data arr param

* add setup() for mint tests + refactor

* remove prints

* scarb fmt

* fix event emit

* uncomment tests libs

* add Swap & SwapCallback

* rename mod

* rename YASRouter contract

* adding tokens into script

* remove unnecessary interface ERC20

* fix lock

* move /tests/erc20 to /contracts/yas_erc20

* add steps into script

* add mint() and swap() into script

* fix u256 params

* add pool balances

* .gitignore add .idea

* refactor initialize_pool()

* refactor mint()

* refactor balance_of()

* add approve() and uncomment erc20 allowance assert

* refactor + fix swap()

* add swap rust docs

* rename :P

* remove unnecessary call

* fix swap + modify script

* fix tests

* scarb fmt

* fix merge error

* add 'make demo-local'

* remove unused imports

* add readme script steps

* fix readme err

* run prettier

---------

Co-authored-by: dpinones <[email protected]>
  • Loading branch information
dubzn and dpinones authored Oct 4, 2023
1 parent ca3ee18 commit d2bf065
Show file tree
Hide file tree
Showing 10 changed files with 772 additions and 36 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
.starkli-wallets/**
target
.env
.idea
4 changes: 4 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,7 @@ url = "2.2.2"
[[bin]]
name = "deploy"
path = "scripts/deploy.rs"

[[bin]]
name = "local"
path = "scripts/local.rs"
3 changes: 3 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ build:

deploy:
cargo run --bin deploy

demo-local:
cargo run --bin local

test:
scarb test
34 changes: 33 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -160,16 +160,18 @@ On Starknet, the deployment process is in two steps:
- Deploying a contract or creating an instance of the previously declared code
with the necessary parameters

1. Build the project:
1. Build the project

```bash
make build
```

2. Start Local Testnet

```bash
make start-katana
```

3. Declare and Deploy: Using [deploy.rs](./scripts/deploy.rs) script, we
sequentially declare and deploy the contracts. Local deployment needs
`katana` running. The account used for deployment is a pre-funded one.
Expand All @@ -186,6 +188,36 @@ On Starknet, the deployment process is in two steps:
make deploy
```

## Run local demo in Katana

This demo will perform the following steps:

- Declaration of the following contracts: ERC20 Token, YASFactory, YASPool, and YASRouter.
- Deployment of 2 ERC20 Tokens, YASFactory, YASPool, and YASRouter.
- Initialization of YASPool with a 1:1 token price.
- Execute approve() for the router to use tokens from the user.
- Execute mint() within the range [-887220, 887220] with 2000000000000000000 tokens.
- Execute swap() exchanging 500000000000000000 of token 0 for token 1.
- Display current balances of both the pool and the user.

1. Build the project

```bash
make build
```

2. Start Local Testnet

```bash
make start-katana
```

3. Run Local Demo

```bash
make demo-local
```

## Override `.env` file

To override environment variables in the `.env` file, you may pass them before
Expand Down
148 changes: 133 additions & 15 deletions scripts/deploy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@ use std::{env, fs};

use dotenv::dotenv;
use eyre::Result;
use starknet::accounts::{Account, ConnectedAccount, ExecutionEncoding, SingleOwnerAccount};
use starknet::accounts::{Account, Call, ConnectedAccount, ExecutionEncoding, SingleOwnerAccount};
use starknet::contract::ContractFactory;
use starknet::core::types::contract::SierraClass;
use starknet::core::utils::get_selector_from_name;
use starknet::core::types::{BlockId, BlockTag, FieldElement, StarknetError};
use starknet::providers::jsonrpc::{HttpTransport, JsonRpcClient};
use starknet::providers::{MaybeUnknownErrorCode, Provider, ProviderError, StarknetErrorWithMessage};
Expand All @@ -15,6 +16,9 @@ const BUILD_PATH_PREFIX: &str = "target/dev/yas_";
// TODO: Update to New once account contracts are migrated to v1
const ENCODING: ExecutionEncoding = ExecutionEncoding::Legacy;

const POSITIVE: bool = false;
const NEGATIVE: bool = true;

/// Create a StarkNet provider.
/// If the `STARKNET_RPC` environment variable is set, it will be used as the RPC URL.
/// Otherwise, the default URL will be used.
Expand Down Expand Up @@ -108,7 +112,7 @@ async fn declare_contract(

// Declare the contract class if it is not already declared.
if !is_already_declared(account.provider(), &class_hash).await? {
println!("==> Declaring Contract: {contract_name}");
println!("\n==> Declaring Contract: {contract_name}");
let flattened_class = contract_artifact.flatten()?;
account.declare(Arc::new(flattened_class), class_hash).send().await?;
println!("Declared Class Hash: {}", format!("{:#064x}", class_hash));
Expand All @@ -117,8 +121,84 @@ async fn declare_contract(
Ok(class_hash)
}

/// Deploy ERC20 Contract.
///
/// # Arguments
///
/// * `name` - The name of the ERC20 token.
/// * `symbol` - The symbol of the ERC20 token.
/// * `total_supply` - The total supply of the ERC20 token.
/// * `recipient` - The initial recipient of the total supply.
///
/// # Returns
///
/// This function returns a `Result` indicating success or an error.
async fn deploy_erc20(
recipient: FieldElement,
account: &SingleOwnerAccount<JsonRpcClient<HttpTransport>, LocalWallet>,
erc20_class_hash: FieldElement
) -> Result<(FieldElement, FieldElement)> {
// Instantiate the contract factory.
let erc20_factory = ContractFactory::new(erc20_class_hash, account);
let unique = true;
let salt = account.get_nonce().await?;
let erc20_token_0_contract_deployment = erc20_factory.deploy(vec![FieldElement::from_hex_be("0x5459415330").unwrap(), FieldElement::from_hex_be("0x2459415330").unwrap(), FieldElement::from_hex_be("0x3782dace9d900000").unwrap(), FieldElement::ZERO, recipient], salt, unique);
let erc20_token_0_deployed_address = erc20_token_0_contract_deployment.deployed_address();
println!("Token TYAS0 Address: {}", format!("{:#064x}", erc20_token_0_deployed_address));
let estimated_fee = erc20_token_0_contract_deployment.estimate_fee().await?.overall_fee * 3 / 2;
erc20_token_0_contract_deployment.max_fee(estimated_fee.into()).send().await?.transaction_hash;

let erc20_token_1_contract_deployment = erc20_factory.deploy(vec![FieldElement::from_hex_be("0x5459415331").unwrap(), FieldElement::from_hex_be("0x2459415331").unwrap(), FieldElement::from_hex_be("0x3782dace9d900000").unwrap(), FieldElement::ZERO, recipient], salt, unique);
let erc20_token_1_deployed_address = erc20_token_1_contract_deployment.deployed_address();
println!("Token TYAS1 Address: {}", format!("{:#064x}", erc20_token_1_deployed_address));
let estimated_fee = erc20_token_1_contract_deployment.estimate_fee().await?.overall_fee * 3 / 2;
erc20_token_1_contract_deployment.max_fee(estimated_fee.into()).send().await?.transaction_hash;

Ok((erc20_token_0_deployed_address, erc20_token_1_deployed_address))
}

/// Asynchronously initializes a liquidity pool using the provided parameters.
///
/// # Arguments
///
/// * `account` - The reference to a `SingleOwnerAccount` with a `JsonRpcClient` and `LocalWallet`.
/// * `pool_address` - The target address of the liquidity pool to be initialized.
/// * `price_sqrt_low` - The lower bound of the square root of the price in the liquidity pool.
/// * `price_sqrt_high` - The upper bound of the square root of the price in the liquidity pool.
/// * `sign` - A boolean flag indicating the sign of the price, where `true` represents negative and `false` represents positive.
///
/// # Returns
///
/// Returns a `Result` indicating success or failure. The `Ok(())` variant is returned on success, and the `Err` variant contains an error description.
async fn initialize_pool(
account: &SingleOwnerAccount<JsonRpcClient<HttpTransport>, LocalWallet>,
pool_address: FieldElement,
price_sqrt_low: u128,
price_sqrt_high: u128,
sign: bool,
) -> Result<()> {
let invoke_result = account
.execute(vec![Call {
to: pool_address,
selector: get_selector_from_name("initialize").unwrap(),
calldata: vec![
// fp mag
FieldElement::from(price_sqrt_low),
FieldElement::from(price_sqrt_high),
// sign
match sign {
NEGATIVE => FieldElement::from(1_u128),
POSITIVE => FieldElement::ZERO,
}
],
}]).send().await?;

println!("Transaction Hash: {}", format!("{:#064x}", invoke_result.transaction_hash));
Ok(())
}

#[tokio::main]
async fn main() -> Result<()> {
pub async fn main() -> Result<()> {
dotenv().ok();

// Create signer from private key.
Expand All @@ -131,28 +211,66 @@ async fn main() -> Result<()> {
let account = initialize_starknet_account(signer, account_address).await?;

// Declare the contract classes if they are not already declared.
let pool_class_hash = declare_contract(&account, "YASPool").await?;
let erc20_class_hash = declare_contract(&account, "ERC20").await?;
let factory_class_hash = declare_contract(&account, "YASFactory").await?;
let pool_class_hash = declare_contract(&account, "YASPool").await?;
let router_class_hash = declare_contract(&account, "YASRouter").await?;

// Instantiate the contract factory.
let salt = account.get_nonce().await?;
let contract_factory = ContractFactory::new(factory_class_hash, account);
let unique = true;
let owner_address = FieldElement::from_hex_be(&env::var("OWNER_ADDRESS").expect("OWNER_ADDRESS not set"))
.expect("Invalid Owner Address");
let unique = true;
println!(
"==> Deploying Factory Contract\nOWNER_ADDRESS: {:#064x}\nPOOL_CLASS_HASH: {:#064x}\nSALT: {}\nUNIQUE: {}",
owner_address, pool_class_hash, salt, unique
);

let contract_deployment = contract_factory.deploy(vec![owner_address, pool_class_hash], salt, unique);
let deployed_address = contract_deployment.deployed_address();
println!("Contract Address: {}", format!("{:#064x}", deployed_address));
println!("\n==> Deploying ERC20 Contracts");
let (token_0, token_1) = deploy_erc20(owner_address, &account, erc20_class_hash).await?;

// Instantiate the contract factory.
println!("\n==> Deploying Factory Contract");
let salt = account.get_nonce().await?;
let yas_factory_contract_factory = ContractFactory::new(factory_class_hash, &account);
let contract_deployment = yas_factory_contract_factory.deploy(vec![owner_address, pool_class_hash], salt, unique);
let factory_address = contract_deployment.deployed_address();
println!("Factory Contract Address: {}", format!("{:#064x}", factory_address));

// Estimate the deployment fee and deploy the contract.
let estimated_fee = contract_deployment.estimate_fee().await?.overall_fee * 3 / 2; // add buffer
let tx = contract_deployment.max_fee(estimated_fee.into()).send().await?.transaction_hash;
println!("Transaction Hash: {}", format!("{:#064x}", tx));

// Instantiate the contract factory.
println!("\n==> Deploying Router Contract");
let salt = account.get_nonce().await?;
let yas_router_contract_factory = ContractFactory::new(router_class_hash, &account);
let contract_deployment = yas_router_contract_factory.deploy(vec![], salt, unique);
let router_address = contract_deployment.deployed_address();
println!("Router Contract Address: {}", format!("{:#064x}", router_address));

// Estimate the deployment fee and deploy the contract.
let estimated_fee = contract_deployment.estimate_fee().await?.overall_fee * 3 / 2; // add buffer
let tx = contract_deployment.max_fee(estimated_fee.into()).send().await?.transaction_hash;
println!("Transaction Hash: {}", format!("{:#064x}", tx));

// Instantiate the contract factory.
println!("\n==> Deploying Pool Contract");
let salt = account.get_nonce().await?;
let yas_pool_contract_factory = ContractFactory::new(pool_class_hash, &account);
let contract_deployment = yas_pool_contract_factory.deploy(vec![factory_address, token_0, token_1, FieldElement::from_hex_be("0x0bb8").unwrap(), FieldElement::from_hex_be("0x3c").unwrap(), FieldElement::ZERO], salt, unique);
let pool_address = contract_deployment.deployed_address();
println!("Pool Contract Address: {}", format!("{:#064x}", pool_address));

// Estimate the deployment fee and deploy the contract.
let estimated_fee = contract_deployment.estimate_fee().await?.overall_fee * 3 / 2; // add buffer
let tx = contract_deployment.max_fee(estimated_fee.into()).send().await?.transaction_hash;
println!("Transaction Hash: {}", format!("{:#064x}", tx));

println!("\n==> Initialize Pool");
initialize_pool(
&account,
pool_address,
// The price of the initial tokens is 1:1 (encode_price_sqrt_1_1)
79228162514264337593543950336,
0,
POSITIVE
).await?;

Ok(())
}
Loading

0 comments on commit d2bf065

Please sign in to comment.