diff --git a/contracts/jstz_native_bridge.mligo b/contracts/jstz_native_bridge.mligo index 8a00fb499..0cadcb76f 100644 --- a/contracts/jstz_native_bridge.mligo +++ b/contracts/jstz_native_bridge.mligo @@ -13,9 +13,8 @@ module Tezos = Tezos.Next -type deposit_request = - { jstz_address: address (* Jstz rollup address. *) - ; l2_address: address +type deposit_request = { + l2_address: address (* L2 address - supports tz1, tz2, tz3, tz4, kt1 *) (* TODO: https://linear.app/tezos/issue/JSTZ-34 Add check for supported addresses @@ -24,6 +23,7 @@ type deposit_request = type storage = { exchanger: address (* Address of exchanger contract minting tez tickets. *) + ; jstz_address: address (* Jstz rollup address. *) ; deposit_request: deposit_request option; (* Address of L2 depositee *) } @@ -39,7 +39,8 @@ type return = operation list * storage Throws if [exchanger] contract does not have %mint entrypoint. This is a fatal bug. *) [@entry] -let deposit (request: deposit_request) ({exchanger; deposit_request}: storage) : return = +let deposit (request: deposit_request) (s: storage) : return = + let { exchanger; jstz_address = _; deposit_request } = s in let () = match deposit_request with | None -> () @@ -54,7 +55,7 @@ let deposit (request: deposit_request) ({exchanger; deposit_request}: storage) : | None -> failwith "Invalid tez ticket contract" | Some contract -> let mint = Tezos.Operation.transaction callback amount contract in - let callback_storage = { exchanger; deposit_request = Some request } in + let callback_storage = { s with deposit_request = Some request } in [ mint ], callback_storage (* [callback ticket {exchanger; deposit_request}] sends a [Deposit_Ticket] @@ -65,13 +66,14 @@ let deposit (request: deposit_request) ({exchanger; deposit_request}: storage) : [deposit_request] is unset at the end of the function. *) [@entry] -let callback (ticket: tez_ticket) ({exchanger; deposit_request}: storage) : return = +let callback (ticket: tez_ticket) (s: storage) : return = +let { exchanger = _ ; jstz_address; deposit_request } = s in let deposit_request = match deposit_request with | None -> failwith "Callback on non-locked deposit" | Some r -> r in - let { jstz_address; l2_address } = deposit_request in + let { l2_address } = deposit_request in let jstz_address: jstz contract = Tezos.get_contract_with_error jstz_address "Invalid rollup address" in @@ -81,5 +83,5 @@ let callback (ticket: tez_ticket) ({exchanger; deposit_request}: storage) : retu 0mutez jstz_address in - let reset_storage = { exchanger; deposit_request = None } in + let reset_storage = { s with deposit_request = None } in [ deposit ], reset_storage diff --git a/contracts/jstz_native_bridge.tz b/contracts/jstz_native_bridge.tz index 672544d7e..9552bc7b7 100644 --- a/contracts/jstz_native_bridge.tz +++ b/contracts/jstz_native_bridge.tz @@ -1,16 +1,16 @@ -{ parameter - (or (ticket %callback (pair nat (option bytes))) - (pair %deposit (address %jstz_address) (address %l2_address))) ; +{ parameter (or (ticket %callback (pair nat (option bytes))) (address %deposit)) ; storage (pair (address %exchanger) - (option %deposit_request (pair (address %jstz_address) (address %l2_address)))) ; + (address %jstz_address) + (option %deposit_request address)) ; code { UNPAIR ; IF_LEFT - { SWAP ; - UNPAIR ; + { DUP 2 ; + UNPAIR 3 ; + DROP ; SWAP ; IF_NONE { PUSH string "Callback on non-locked deposit" ; FAILWITH } {} ; - UNPAIR ; + SWAP ; CONTRACT (or (pair %deposit_ticket address (ticket (pair nat (option bytes)))) (pair %deposit_fa_ticket @@ -19,20 +19,22 @@ (ticket %ticket (pair nat (option bytes))))) ; IF_NONE { PUSH string "Invalid rollup address" ; FAILWITH } {} ; PUSH mutez 0 ; - DIG 4 ; + DIG 3 ; DIG 3 ; PAIR ; LEFT (pair address (option address) (ticket (pair nat (option bytes)))) ; TRANSFER_TOKENS ; - NONE (pair address address) ; - DIG 2 ; - PAIR ; + SWAP ; + NONE address ; + UPDATE 4 ; NIL operation ; DIG 2 ; CONS ; PAIR } - { SWAP ; - UNPAIR ; + { DUP 2 ; + UNPAIR 3 ; + SWAP ; + DROP ; SWAP ; IF_NONE {} { DROP ; PUSH string "Deposit locked" ; FAILWITH } ; AMOUNT ; @@ -40,21 +42,20 @@ DUP 2 ; COMPARE ; LE ; - IF { DROP 3 ; + IF { DROP 4 ; PUSH string "Invalid deposit amount: Deposit amount must be greater than 0." ; FAILWITH } { SELF %callback ; ADDRESS ; - DUP 3 ; + DIG 2 ; CONTRACT %mint address ; IF_NONE { DROP 4 ; PUSH string "Invalid tez ticket contract" ; FAILWITH } { DUG 2 ; TRANSFER_TOKENS ; - DIG 2 ; + DUG 2 ; SOME ; - DIG 2 ; - PAIR ; + UPDATE 4 ; NIL operation ; DIG 2 ; CONS ; diff --git a/contracts/test/test_jstz_native_bridge.mligo b/contracts/test/test_jstz_native_bridge.mligo index f52f5d992..ef83e88bc 100644 --- a/contracts/test/test_jstz_native_bridge.mligo +++ b/contracts/test/test_jstz_native_bridge.mligo @@ -12,6 +12,8 @@ (* Sets up the exchanger, bridge, mock jstz rollup, l2 address and deposit request *) let setup_contracts_and_request () = + let jstz_rollup = init_jstz_rollup () in + let jstz_address = Test.Next.Typed_address.to_address jstz_rollup.taddr in let exchanger = let exchanger_contract = Test.Next.Originate.contract @@ -24,47 +26,45 @@ let setup_contracts_and_request () = let bridge = Test.Next.Originate.contract (contract_of Jstz_native_bridge) - { exchanger; deposit_request = None} + { exchanger; jstz_address; deposit_request = None} 0tez in - let jstz_rollup = init_jstz_rollup () in let l2_address = Test.Next.Account.bob () in let deposit_request = { - jstz_address = (Test.Next.Typed_address.to_address jstz_rollup.taddr); l2_address; } in - { exchanger; bridge; jstz_rollup; l2_address; deposit_request } + { exchanger; bridge; jstz_rollup; jstz_address; l2_address; deposit_request } let test_deposit_successful = - let { exchanger; bridge; jstz_rollup; l2_address; deposit_request } = setup_contracts_and_request () in + let { exchanger; bridge; jstz_rollup; jstz_address; l2_address; deposit_request } = setup_contracts_and_request () in let _ = Test.Next.Typed_address.transfer_exn bridge.taddr (Deposit deposit_request) 100tez in let expected: normalized_deposit list = [ (l2_address, exchanger, (0n, None), 100000000n) ] in let { native_deposit = actual; fa_deposit = _; } = Test.Next.get_storage jstz_rollup.taddr in let () = assert_lists (fun x y -> assert (Test.equal x y)) expected actual in let bridge_storage = Test.Next.get_storage bridge.taddr in - assert (Test.equal bridge_storage { exchanger; deposit_request = None }) + assert (Test.equal bridge_storage { exchanger; jstz_address; deposit_request = None }) let test_deposit_deposit_request_locked_throws_error = - let { exchanger; bridge = _; jstz_rollup = _; l2_address = _; deposit_request } = setup_contracts_and_request () in + let { exchanger; bridge = _; jstz_rollup = _; jstz_address; l2_address = _; deposit_request } = setup_contracts_and_request () in let bridge = // Override the bridge initial storage Test.Next.Originate.contract (contract_of Jstz_native_bridge) - { exchanger; deposit_request = Some deposit_request} + { exchanger; jstz_address; deposit_request = Some deposit_request} 0tez in let result = Test.Next.Typed_address.transfer bridge.taddr (Deposit deposit_request) 100tez in assert_failed result "Expected error when deposit request is locked" let test_callback_direct_call_throws_error = - let { exchanger = _; bridge; jstz_rollup = _; l2_address = _; deposit_request = _ } = setup_contracts_and_request () in + let ctx = setup_contracts_and_request () in let ticket : tez_ticket = Option.value_exn "Failed to create ticket" (Tezos.Next.Ticket.create (0n, None) 100n) in - let result = Test.Next.Typed_address.transfer bridge.taddr (Callback ticket) 0tez in + let result = Test.Next.Typed_address.transfer ctx.bridge.taddr (Callback ticket) 0tez in assert_failed result "Expected error when callback is called directly" diff --git a/crates/jstz_cli/src/bridge/deposit.rs b/crates/jstz_cli/src/bridge/deposit.rs index 5eba9af1b..8b7d058c2 100644 --- a/crates/jstz_cli/src/bridge/deposit.rs +++ b/crates/jstz_cli/src/bridge/deposit.rs @@ -25,7 +25,6 @@ pub fn exec( } let to_pkh = to.resolve(&cfg)?; - debug!("resolved `to` -> {:?}", to_pkh); // Check if trying to deposit to a bootsrap account. if let Some(bootstrap_account) = SANDBOX_BOOTSTRAP_ACCOUNTS @@ -37,27 +36,26 @@ pub fn exec( bootstrap_account.address ); } + let pkh = to_pkh.to_base58(); + debug!("resolved `to` -> {}", &pkh); // Execute the octez-client command if cfg .octez_client(&network)? .call_contract( &from, - "jstz_bridge", + "jstz_native_bridge", "deposit", - &format!( - "(Pair 0x{} {})", - hex::encode_upper(to_pkh.as_bytes()), - amount, - ), + &format!("\"{}\"", &pkh), + amount, ) .is_err() { - bail_user_error!("Failed to deposit CTEZ. Please check whether the addresses and network are correct."); + bail_user_error!("Failed to deposit XTZ. Please check whether the addresses and network are correct."); } info!( - "Deposited {} CTEZ from {} to {}", + "Deposited {} XTZ from {} to {}", amount, from, to.to_string() diff --git a/crates/jstz_cli/src/bridge/mod.rs b/crates/jstz_cli/src/bridge/mod.rs index bb2ec1a1b..eca75eb8c 100644 --- a/crates/jstz_cli/src/bridge/mod.rs +++ b/crates/jstz_cli/src/bridge/mod.rs @@ -6,7 +6,7 @@ use crate::{config::NetworkName, error::Result, utils::AddressOrAlias}; #[derive(Debug, Subcommand)] pub enum Command { - /// 💰 Deposits CTEZ from an existing Tezos L1 address to a jstz address. + /// 💰 Deposits XTZ from an existing Tezos L1 address to a jstz address. Deposit { /// Tezos L1 address or alias to withdraw from (must be stored in octez-client's wallet). #[arg(short, long)] @@ -14,7 +14,7 @@ pub enum Command { /// jstz address or alias to deposit to. #[arg(short, long)] to: AddressOrAlias, - /// The amount in CTEZ to transfer. + /// The amount in XTZ to transfer. #[arg(short, long)] amount: u64, /// Specifies the network from the config file, defaulting to the configured default network. diff --git a/crates/jstz_cli/src/main.rs b/crates/jstz_cli/src/main.rs index 016cc934c..0855cd1ee 100644 --- a/crates/jstz_cli/src/main.rs +++ b/crates/jstz_cli/src/main.rs @@ -64,7 +64,7 @@ enum Command { #[arg(short, long)] trace: bool, }, - /// 🌉 Move CTEZ between L1 and jstz with the jstz bridge {n} + /// 🌉 Move XTZ between L1 and jstz with the jstz bridge {n} #[command(subcommand)] Bridge(bridge::Command), diff --git a/crates/jstz_cli/src/sandbox/daemon.rs b/crates/jstz_cli/src/sandbox/daemon.rs index d8b9b4efe..8c89f44eb 100644 --- a/crates/jstz_cli/src/sandbox/daemon.rs +++ b/crates/jstz_cli/src/sandbox/daemon.rs @@ -494,6 +494,7 @@ fn start_sandbox( let config = Rc::new(RefCell::new(cfg.clone())); let logs = Rc::new(RefCell::new(log_file.try_clone()?)); let mut sandbox = Sandbox::new(config, logs); + // 1. Init node init_node(log_file, progress, cfg)?; @@ -518,21 +519,12 @@ fn start_sandbox( sandbox.set_baker(baker)?; debug!(log_file, "Started baker (using octez-client)"); - // 5. Deploy bridge - progress_step(log_file, progress); - debug!(log_file, "Deploying bridge..."); + // 4.1 Deploy the XTZ ticket exchanger let client = cfg.octez_client_sandbox()?; - let exchanger = Exchanger::deploy(&client, OPERATOR_ADDRESS)?; debug!(log_file, "Exchanger deployed at {}", exchanger); - progress_step(log_file, progress); - - let bridge = NativeBridge::deploy(&client, OPERATOR_ADDRESS, &exchanger)?; - - debug!(log_file, "Bridge deployed at {}", bridge); - - // 6. Create an installer kernel + // 5. Create an installer kernel progress_step(log_file, progress); debug!(log_file, "Creating installer kernel..."); @@ -544,13 +536,13 @@ fn start_sandbox( "Installer kernel created with preimages at {:?}", preimages_dir ); - // 7. Originate the rollup + // 6. Originate the rollup progress_step(log_file, progress); let rollup = JstzRollup::deploy(&cfg.octez_client_sandbox()?, OPERATOR_ADDRESS, &installer)?; debug!(log_file, "`jstz_rollup` originated at {}", rollup); - // 8. As a thread, start rollup node + // 7. As a thread, start rollup node progress_step(log_file, progress); debug!(log_file, "Starting rollup node..."); @@ -566,6 +558,16 @@ fn start_sandbox( sandbox.set_rollup_node(rollup_node)?; debug!(log_file, "Started octez-smart-rollup-node"); + // 8. Deploy bridge + progress_step(log_file, progress); + debug!(log_file, "Deploying bridge..."); + + progress_step(log_file, progress); + + let bridge = NativeBridge::deploy(&client, OPERATOR_ADDRESS, &exchanger, &rollup)?; + + debug!(log_file, "Bridge deployed at {}", bridge); + // 9. Set the rollup address in the bridge progress_step(log_file, progress); debug!(log_file, "`jstz_bridge` `rollup` address set to {}", rollup); diff --git a/crates/jstz_rollup/src/bridge.rs b/crates/jstz_rollup/src/bridge.rs index f46f84f88..88d420b97 100644 --- a/crates/jstz_rollup/src/bridge.rs +++ b/crates/jstz_rollup/src/bridge.rs @@ -70,8 +70,10 @@ impl NativeBridge { client: &OctezClient, operator: &str, exchanger: &Exchanger, + rollup_address: &str, ) -> Result { - let storage_init = format!("(Pair \"{}\" None)", exchanger.0); + let storage_init = + format!("(Pair \"{}\" \"{}\" None)", exchanger.0, rollup_address); client .originate_contract( "jstz_native_bridge", @@ -133,6 +135,7 @@ impl BridgeContract { &self.0, "set_rollup", &format!("\"{}\"", rollup_address), + 0, ) } } diff --git a/crates/octez/src/client.rs b/crates/octez/src/client.rs index 62eb6117a..9b091a46c 100644 --- a/crates/octez/src/client.rs +++ b/crates/octez/src/client.rs @@ -119,10 +119,11 @@ impl OctezClient { to: &str, entrypoint: &str, parameter: &str, + amount: u64, ) -> Result<()> { run_command(self.command().args([ "transfer", - "0", + &format!("{}", amount), "from", from, "to",